From 3c87cb3ad26f259e5af80c0dfddcb329d70fb8b7 Mon Sep 17 00:00:00 2001 From: Lucas Wojciechowski Date: Tue, 5 Jan 2016 17:10:09 -0800 Subject: [PATCH 01/32] Upgrade mapbox-gl-function and mapbox-gl-style-spec --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 9def37b0d17..4eb671842b1 100644 --- a/package.json +++ b/package.json @@ -17,8 +17,8 @@ "geojson-vt": "^2.1.0", "gl-matrix": "^2.3.1", "grid-index": "^0.1.0", - "mapbox-gl-function": "^1.0.0", - "mapbox-gl-style-spec": "^8.4.2", + "mapbox-gl-function": "^1.1.0", + "mapbox-gl-style-spec": "^8.5.0", "minifyify": "^7.0.1", "pbf": "^1.3.2", "pngjs": "^2.2.0", From 68f7da0727079cea63623016178226e64054a22f Mon Sep 17 00:00:00 2001 From: Lucas Wojciechowski Date: Tue, 19 Jan 2016 17:11:48 -0800 Subject: [PATCH 02/32] Change argument signature of calculate --- js/data/bucket/circle_bucket.js | 1 - js/data/bucket/symbol_bucket.js | 8 ++-- js/style/style.js | 1 + js/style/style_declaration.js | 16 +++++--- js/style/style_function.js | 25 +++++++++++ js/style/style_layer.js | 12 +++--- js/style/style_layer/line_style_layer.js | 8 ++-- js/style/style_layer/symbol_style_layer.js | 6 +-- js/style/style_transition.js | 15 ++++--- test/js/style/style_declaration.test.js | 48 +++++++++++++--------- 10 files changed, 91 insertions(+), 49 deletions(-) create mode 100644 js/style/style_function.js diff --git a/js/data/bucket/circle_bucket.js b/js/data/bucket/circle_bucket.js index bcf8d331f50..d7b36d84d32 100644 --- a/js/data/bucket/circle_bucket.js +++ b/js/data/bucket/circle_bucket.js @@ -40,7 +40,6 @@ CircleBucket.prototype.programInterfaces = { }; CircleBucket.prototype.addFeature = function(feature) { - var geometries = loadGeometry(feature); for (var j = 0; j < geometries.length; j++) { var geometry = geometries[j]; diff --git a/js/data/bucket/symbol_bucket.js b/js/data/bucket/symbol_bucket.js index 21898fcc938..e5036a3da7c 100644 --- a/js/data/bucket/symbol_bucket.js +++ b/js/data/bucket/symbol_bucket.js @@ -122,10 +122,10 @@ SymbolBucket.prototype.populateBuffers = function(collisionTile, stacks, icons) // This calculates text-size at a high zoom level so that all tiles can // use the same value when calculating anchor positions. var zoomHistory = { lastIntegerZoom: Infinity, lastIntegerZoomTime: 0, lastZoom: 0 }; - this.adjustedTextMaxSize = this.layer.getLayoutValue('text-size', 18, zoomHistory); - this.adjustedTextSize = this.layer.getLayoutValue('text-size', this.zoom + 1, zoomHistory); - this.adjustedIconMaxSize = this.layer.getLayoutValue('icon-size', 18, zoomHistory); - this.adjustedIconSize = this.layer.getLayoutValue('icon-size', this.zoom + 1, zoomHistory); + this.adjustedTextMaxSize = this.layer.getLayoutValue('text-size', {zoom: 18, zoomHistory: zoomHistory}); + this.adjustedTextSize = this.layer.getLayoutValue('text-size', {zoom: this.zoom + 1, zoomHistory: zoomHistory}); + this.adjustedIconMaxSize = this.layer.getLayoutValue('icon-size', {zoom: 18, zoomHistory: zoomHistory}); + this.adjustedIconSize = this.layer.getLayoutValue('icon-size', {zoom: this.zoom + 1, zoomHistory: zoomHistory}); var tileSize = 512 * this.overscaling; this.tilePixelRatio = EXTENT / tileSize; diff --git a/js/style/style.js b/js/style/style.js index ba7ca44b374..b0737b0e93f 100644 --- a/js/style/style.js +++ b/js/style/style.js @@ -545,6 +545,7 @@ Style.prototype = util.inherit(Evented, { if (util.deepEqual(layer.getPaintProperty(name, klass), value)) return this; layer.setPaintProperty(name, value, klass); + return this.updateClasses(layerId, name); }, diff --git a/js/style/style_declaration.js b/js/style/style_declaration.js index c7d55abe3cb..97b120dde14 100644 --- a/js/style/style_declaration.js +++ b/js/style/style_declaration.js @@ -1,6 +1,6 @@ 'use strict'; -var MapboxGLFunction = require('mapbox-gl-function'); +var MapboxGLFunction = require('./style_function'); var parseColor = require('./parse_color'); module.exports = StyleDeclaration; @@ -26,7 +26,11 @@ function StyleDeclaration(reference, value) { } function transitioned(calculate) { - return function(z, zh, duration) { + return function(globalProperties, featureProperties) { + var z = globalProperties.zoom; + var zh = globalProperties.zoomHistory; + var duration = globalProperties.duration; + var fraction = z % 1; var t = Math.min((Date.now() - zh.lastIntegerZoomTime) / duration, 1); var fromScale = 1; @@ -36,12 +40,12 @@ function transitioned(calculate) { if (z > zh.lastIntegerZoom) { mix = fraction + (1 - fraction) * t; fromScale *= 2; - from = calculate(z - 1); - to = calculate(z); + from = calculate({zoom: z - 1}, featureProperties); + to = calculate({zoom: z}, featureProperties); } else { mix = 1 - (1 - t) * fraction; - to = calculate(z); - from = calculate(z + 1); + to = calculate({zoom: z}, featureProperties); + from = calculate({zoom: z + 1}, featureProperties); fromScale /= 2; } diff --git a/js/style/style_function.js b/js/style/style_function.js new file mode 100644 index 00000000000..e2972cdb20a --- /dev/null +++ b/js/style/style_function.js @@ -0,0 +1,25 @@ +'use strict'; + +var MapboxGLFunction = require('mapbox-gl-function'); + +exports.interpolated = function(parameters) { + var inner = MapboxGLFunction.interpolated(parameters); + var outer = function(globalProperties, featureProperties) { + return inner(globalProperties && globalProperties.zoom, featureProperties || {}); + }; + outer.isFeatureConstant = inner.isFeatureConstant; + outer.isGlobalConstant = inner.isGlobalConstant; + return outer; +}; + +exports['piecewise-constant'] = function(parameters) { + var inner = MapboxGLFunction['piecewise-constant'](parameters); + var outer = function(globalProperties, featureProperties) { + return inner(globalProperties && globalProperties.zoom, featureProperties || {}); + }; + outer.isFeatureConstant = inner.isFeatureConstant; + outer.isGlobalConstant = inner.isGlobalConstant; + return outer; +}; + +exports.isFunctionDefinition = MapboxGLFunction.isFunctionDefinition; diff --git a/js/style/style_layer.js b/js/style/style_layer.js index 9fd506b1b91..8b3038c5018 100644 --- a/js/style/style_layer.js +++ b/js/style/style_layer.js @@ -99,12 +99,12 @@ StyleLayer.prototype = util.inherit(Evented, { ); }, - getLayoutValue: function(name, zoom, zoomHistory) { + getLayoutValue: function(name, globalProperties, featureProperties) { var specification = this._layoutSpecifications[name]; var declaration = this._layoutDeclarations[name]; if (declaration) { - return declaration.calculate(zoom, zoomHistory); + return declaration.calculate(globalProperties, featureProperties); } else { return specification.default; } @@ -152,12 +152,12 @@ StyleLayer.prototype = util.inherit(Evented, { } }, - getPaintValue: function(name, zoom, zoomHistory) { + getPaintValue: function(name, globalProperties, featureProperties) { var specification = this._paintSpecifications[name]; var transition = this._paintTransitions[name]; if (transition) { - return transition.at(zoom, zoomHistory); + return transition.calculate(globalProperties, featureProperties); } else if (specification.type === 'color' && specification.default) { return parseColor(specification.default); } else { @@ -200,10 +200,10 @@ StyleLayer.prototype = util.inherit(Evented, { // update all zoom-dependent layout/paint values recalculate: function(zoom, zoomHistory) { for (var paintName in this._paintTransitions) { - this.paint[paintName] = this.getPaintValue(paintName, zoom, zoomHistory); + this.paint[paintName] = this.getPaintValue(paintName, {zoom: zoom, zoomHistory: zoomHistory}); } for (var layoutName in this._layoutFunctions) { - this.layout[layoutName] = this.getLayoutValue(layoutName, zoom, zoomHistory); + this.layout[layoutName] = this.getLayoutValue(layoutName, {zoom: zoom, zoomHistory: zoomHistory}); } }, diff --git a/js/style/style_layer/line_style_layer.js b/js/style/style_layer/line_style_layer.js index f50ecfd07a6..28104abec27 100644 --- a/js/style/style_layer/line_style_layer.js +++ b/js/style/style_layer/line_style_layer.js @@ -11,16 +11,16 @@ module.exports = LineStyleLayer; LineStyleLayer.prototype = util.inherit(StyleLayer, { - getPaintValue: function(name, zoom, zoomHistory) { - var value = StyleLayer.prototype.getPaintValue.call(this, name, zoom, zoomHistory); + getPaintValue: function(name, globalProperties, featureProperties) { + var value = StyleLayer.prototype.getPaintValue.apply(this, arguments); // If the line is dashed, scale the dash lengths by the line // width at the previous round zoom level. if (value && name === 'line-dasharray') { - var flooredZoom = Math.floor(zoom); + var flooredZoom = Math.floor(globalProperties.zoom); if (this._flooredZoom !== flooredZoom) { this._flooredZoom = flooredZoom; - this._flooredLineWidth = this.getPaintValue('line-width', flooredZoom, Infinity); + this._flooredLineWidth = this.getPaintValue('line-width', globalProperties, featureProperties); } value.fromScale *= this._flooredLineWidth; diff --git a/js/style/style_layer/symbol_style_layer.js b/js/style/style_layer/symbol_style_layer.js index ce469ae85d5..3ebf4412d48 100644 --- a/js/style/style_layer/symbol_style_layer.js +++ b/js/style/style_layer/symbol_style_layer.js @@ -21,13 +21,13 @@ SymbolStyleLayer.prototype = util.inherit(StyleLayer, { return false; }, - getLayoutValue: function(name, zoom, zoomHistory) { + getLayoutValue: function(name, globalProperties, featureProperties) { if (name === 'text-rotation-alignment' && - this.getLayoutValue('symbol-placement', zoom, zoomHistory) === 'line' && + this.getLayoutValue('symbol-placement', globalProperties, featureProperties) === 'line' && !this.getLayoutProperty('text-rotation-alignment')) { return 'map'; } else if (name === 'icon-rotation-alignment' && - this.getLayoutValue('symbol-placement', zoom, zoomHistory) === 'line' && + this.getLayoutValue('symbol-placement', globalProperties, featureProperties) === 'line' && !this.getLayoutProperty('icon-rotation-alignment')) { return 'map'; } else { diff --git a/js/style/style_transition.js b/js/style/style_transition.js index d5581c8c204..ced8ef6054f 100644 --- a/js/style/style_transition.js +++ b/js/style/style_transition.js @@ -44,16 +44,21 @@ StyleTransition.prototype.instant = function() { /* * Return the value of the transitioning property at zoom level `z` and optional time `t` */ -StyleTransition.prototype.at = function(z, zoomHistory, t) { - - var value = this.declaration.calculate(z, zoomHistory, this.duration); +StyleTransition.prototype.calculate = function(globalProperties, featureProperties) { + var value = this.declaration.calculate( + util.extend({}, globalProperties, {duration: this.duration}), + featureProperties + ); if (this.instant()) return value; - t = t || Date.now(); + var t = globalProperties.time || Date.now(); if (t < this.endTime) { - var oldValue = this.oldTransition.at(z, zoomHistory, this.startTime); + var oldValue = this.oldTransition.calculate( + util.extend({}, globalProperties, {time: this.startTime}), + featureProperties + ); var eased = this.ease((t - this.startTime - this.delay) / this.duration); value = this.interp(oldValue, value, eased); } diff --git a/test/js/style/style_declaration.test.js b/test/js/style/style_declaration.test.js index 6da6ba1bf35..aea9ec1958f 100644 --- a/test/js/style/style_declaration.test.js +++ b/test/js/style/style_declaration.test.js @@ -5,27 +5,27 @@ var StyleDeclaration = require('../../../js/style/style_declaration'); test('StyleDeclaration', function(t) { t.test('constant', function(t) { - t.equal((new StyleDeclaration({type: "number"}, 5)).calculate(0), 5); - t.equal((new StyleDeclaration({type: "number"}, 5)).calculate(100), 5); + t.equal((new StyleDeclaration({type: "number"}, 5)).calculate({zoom: 0}), 5); + t.equal((new StyleDeclaration({type: "number"}, 5)).calculate({zoom: 100}), 5); t.end(); }); t.test('interpolated functions', function(t) { var reference = {type: "number", function: "interpolated"}; - t.equal((new StyleDeclaration(reference, { stops: [[0, 1]] })).calculate(0), 1); - t.equal((new StyleDeclaration(reference, { stops: [[2, 2], [5, 10]] })).calculate(0), 2); - t.equal((new StyleDeclaration(reference, { stops: [[0, 0], [5, 10]] })).calculate(12), 10); - t.equal((new StyleDeclaration(reference, { stops: [[0, 0], [5, 10]] })).calculate(6), 10); - t.equal(Math.round((new StyleDeclaration(reference, { stops: [[0, 0], [5, 10]], base: 1.01 })).calculate(2.5)), 5); - t.equal((new StyleDeclaration(reference, { stops: [[0, 0], [1, 10], [2, 20]] })).calculate(2), 20); - t.equal((new StyleDeclaration(reference, { stops: [[0, 0], [1, 10], [2, 20]] })).calculate(1), 10); - t.equal((new StyleDeclaration(reference, { stops: [[0, 0]] })).calculate(6), 0); + t.equal((new StyleDeclaration(reference, { stops: [[0, 1]] })).calculate({zoom: 0}), 1); + t.equal((new StyleDeclaration(reference, { stops: [[2, 2], [5, 10]] })).calculate({zoom: 0}), 2); + t.equal((new StyleDeclaration(reference, { stops: [[0, 0], [5, 10]] })).calculate({zoom: 12}), 10); + t.equal((new StyleDeclaration(reference, { stops: [[0, 0], [5, 10]] })).calculate({zoom: 6}), 10); + t.equal(Math.round((new StyleDeclaration(reference, { stops: [[0, 0], [5, 10]], base: 1.01 })).calculate({zoom: 2.5})), 5); + t.equal((new StyleDeclaration(reference, { stops: [[0, 0], [1, 10], [2, 20]] })).calculate({zoom: 2}), 20); + t.equal((new StyleDeclaration(reference, { stops: [[0, 0], [1, 10], [2, 20]] })).calculate({zoom: 1}), 10); + t.equal((new StyleDeclaration(reference, { stops: [[0, 0]] })).calculate({zoom: 6}), 0); t.end(); }); t.test('non-interpolated piecewise-constant function', function(t) { var decl = new StyleDeclaration({type: "array", function: "piecewise-constant"}, {stops: [[0, [0, 10, 5]]]}); - t.deepEqual(decl.calculate(0), [0, 10, 5]); + t.deepEqual(decl.calculate({zoom: 0}), [0, 10, 5]); t.end(); }); @@ -33,21 +33,29 @@ test('StyleDeclaration', function(t) { var reference = {type: "image", function: "piecewise-constant", transition: true}; var constant = new StyleDeclaration(reference, 'a.png'); - t.deepEqual(constant.calculate(0, { lastIntegerZoomTime: 0, lastIntegerZoom: 0 }, 300), - { to: 'a.png', toScale: 1, from: 'a.png', fromScale: 0.5, t: 1 }); + t.deepEqual( + constant.calculate({zoom: 0, zoomHistory: { lastIntegerZoomTime: 0, lastIntegerZoom: 0 }, duration: 300}), + { to: 'a.png', toScale: 1, from: 'a.png', fromScale: 0.5, t: 1 } + ); var variable = new StyleDeclaration(reference, {stops: [[0, 'a.png'], [1, 'b.png']]}); - t.deepEqual(variable.calculate(1, { lastIntegerZoomTime: 0, lastIntegerZoom: 0 }, 300), - { to: 'b.png', toScale: 1, from: 'a.png', fromScale: 2, t: 1 }); + t.deepEqual( + variable.calculate({ + zoom: 1, + zoomHistory: { lastIntegerZoomTime: 0, lastIntegerZoom: 0 }, + duration: 300 + }), + { to: 'b.png', toScale: 1, from: 'a.png', fromScale: 2, t: 1 } + ); t.end(); }); t.test('color parsing', function(t) { var reference = {type: "color", function: "interpolated"}; - t.deepEqual(new StyleDeclaration(reference, 'red').calculate(0), [ 1, 0, 0, 1 ]); - t.deepEqual(new StyleDeclaration(reference, '#ff00ff').calculate(0), [ 1, 0, 1, 1 ]); - t.deepEqual(new StyleDeclaration(reference, { stops: [[0, '#f00'], [1, '#0f0']] }).calculate(0), [1, 0, 0, 1]); + t.deepEqual(new StyleDeclaration(reference, 'red').calculate({zoom: 0}), [ 1, 0, 0, 1 ]); + t.deepEqual(new StyleDeclaration(reference, '#ff00ff').calculate({zoom: 0}), [ 1, 0, 1, 1 ]); + t.deepEqual(new StyleDeclaration(reference, { stops: [[0, '#f00'], [1, '#0f0']] }).calculate({zoom: 0}), [1, 0, 0, 1]); t.throws(function () { t.ok(new StyleDeclaration(reference, { stops: [[0, '#f00'], [1, null]] })); }, /Invalid color/); @@ -56,8 +64,8 @@ test('StyleDeclaration', function(t) { t.ok(new StyleDeclaration(reference, '#00000')); }, Error, /Invalid color/i); // cached - t.deepEqual(new StyleDeclaration(reference, '#ff00ff').calculate(0), [ 1, 0, 1, 1 ]); - t.deepEqual(new StyleDeclaration(reference, 'rgba(255, 51, 0, 1)').calculate(0), [ 1, 0.2, 0, 1 ]); + t.deepEqual(new StyleDeclaration(reference, '#ff00ff').calculate({zoom: 0}), [ 1, 0, 1, 1 ]); + t.deepEqual(new StyleDeclaration(reference, 'rgba(255, 51, 0, 1)').calculate({zoom: 0}), [ 1, 0.2, 0, 1 ]); t.end(); }); From c991e8f306215801164a966b91ba5386898a2d7b Mon Sep 17 00:00:00 2001 From: Lucas Wojciechowski Date: Wed, 6 Jan 2016 10:24:30 -0800 Subject: [PATCH 03/32] Attribute-ify circle shaders --- js/render/draw_circle.js | 8 ++++++-- shaders/circle.fragment.glsl | 4 ++-- shaders/circle.vertex.glsl | 4 ++++ 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/js/render/draw_circle.js b/js/render/draw_circle.js index e34cc0a1ee7..92593a7fcce 100644 --- a/js/render/draw_circle.js +++ b/js/render/draw_circle.js @@ -25,11 +25,13 @@ function drawCircles(painter, source, layer, coords) { // are inversely related. var antialias = 1 / browser.devicePixelRatio / layer.paint['circle-radius']; - var color = util.premultiply(layer.paint['circle-color'], layer.paint['circle-opacity']); - gl.uniform4fv(program.u_color, color); gl.uniform1f(program.u_blur, Math.max(layer.paint['circle-blur'], antialias)); gl.uniform1f(program.u_size, layer.paint['circle-radius']); + var color = util.premultiply(layer.paint['circle-color'], layer.paint['circle-opacity']).map(function(v) { return v * 255; }); + gl.disableVertexAttribArray(program.a_color); + gl.vertexAttrib4fv(program.a_color, color); + for (var i = 0; i < coords.length; i++) { var coord = coords[i]; @@ -64,4 +66,6 @@ function drawCircles(painter, source, layer, coords) { gl.drawElements(gl.TRIANGLES, count, gl.UNSIGNED_SHORT, elementOffset); } } + + gl.enableVertexAttribArray(program.a_color); } diff --git a/shaders/circle.fragment.glsl b/shaders/circle.fragment.glsl index 97e0348c43d..4874a024abc 100644 --- a/shaders/circle.fragment.glsl +++ b/shaders/circle.fragment.glsl @@ -1,11 +1,11 @@ precision mediump float; -uniform lowp vec4 u_color; uniform lowp float u_blur; +varying lowp vec4 v_color; varying vec2 v_extrude; void main() { float t = smoothstep(1.0 - u_blur, 1.0, length(v_extrude)); - gl_FragColor = u_color * (1.0 - t); + gl_FragColor = v_color * (1.0 - t); } diff --git a/shaders/circle.vertex.glsl b/shaders/circle.vertex.glsl index 59f4bb24adf..8ae53c97de3 100644 --- a/shaders/circle.vertex.glsl +++ b/shaders/circle.vertex.glsl @@ -5,8 +5,10 @@ uniform mat4 u_exmatrix; uniform mediump float u_size; attribute vec2 a_pos; +attribute lowp vec4 a_color; varying vec2 v_extrude; +varying lowp vec4 v_color; void main(void) { // unencode the extrusion vector that we snuck into the a_pos vector @@ -20,4 +22,6 @@ void main(void) { // gl_Position is divided by gl_Position.w after this shader runs. // Multiply the extrude by it so that it isn't affected by it. gl_Position += extrude * gl_Position.w; + + v_color = a_color / 255.0; } From 7d3725c4d553874fabd494db7ba03d02c08278d0 Mon Sep 17 00:00:00 2001 From: Lucas Wojciechowski Date: Mon, 11 Jan 2016 12:17:29 -0800 Subject: [PATCH 04/32] Support array attributes and attribute multipliers in Bucket --- js/data/bucket.js | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/js/data/bucket.js b/js/data/bucket.js index 6d3fb1a21d0..90c4a2301ed 100644 --- a/js/data/bucket.js +++ b/js/data/bucket.js @@ -244,12 +244,37 @@ Bucket.prototype.createFilter = function() { var createVertexAddMethodCache = {}; function createVertexAddMethod(programName, programInterface, bufferName) { + var body = ''; + var pushArgs = []; for (var i = 0; i < programInterface.attributes.length; i++) { - pushArgs = pushArgs.concat(programInterface.attributes[i].value); + var attribute = programInterface.attributes[i]; + + var attributePushArgs = []; + if (Array.isArray(attribute.value)) { + attributePushArgs = attribute.value; + } else { + var attributeId = '_' + i; + body += 'var ' + attributeId + ' = ' + attribute.value + ';'; + for (var j = 0; j < attribute.components; j++) { + attributePushArgs.push(attributeId + '[' + j + ']'); + } + } + + var multipliedAttributePushArgs; + if (attribute.multiplier) { + multipliedAttributePushArgs = []; + for (var k = 0; k < attributePushArgs.length; k++) { + multipliedAttributePushArgs[k] = attributePushArgs[k] + '*' + attribute.multiplier; + } + } else { + multipliedAttributePushArgs = attributePushArgs; + } + + pushArgs = pushArgs.concat(multipliedAttributePushArgs); } - var body = 'return this.arrays.' + bufferName + '.emplaceBack(' + pushArgs.join(', ') + ');'; + body += 'return this.arrays.' + bufferName + '.emplaceBack(' + pushArgs.join(',') + ');'; if (!createVertexAddMethodCache[body]) { createVertexAddMethodCache[body] = new Function(programInterface.attributeArgs, body); From 479d9e64eda32276bd5966a64cd284c02214052b Mon Sep 17 00:00:00 2001 From: Lucas Wojciechowski Date: Mon, 11 Jan 2016 12:02:59 -0800 Subject: [PATCH 05/32] Set circle color in the buffer --- js/data/bucket.js | 3 +++ js/data/bucket/circle_bucket.js | 11 +++++++++++ js/render/draw_circle.js | 5 ----- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/js/data/bucket.js b/js/data/bucket.js index 90c4a2301ed..de55f574e2f 100644 --- a/js/data/bucket.js +++ b/js/data/bucket.js @@ -231,6 +231,7 @@ Bucket.prototype.serialize = function() { Bucket.prototype.createStyleLayer = function() { if (!(this.layer instanceof StyleLayer)) { this.layer = StyleLayer.create(this.layer); + this.layer.updatePaintTransitions([], {transition: false}); this.layer.recalculate(this.zoom, { lastIntegerZoom: Infinity, lastIntegerZoomTime: 0, lastZoom: 0 }); } }; @@ -241,6 +242,8 @@ Bucket.prototype.createFilter = function() { } }; +Bucket.prototype._premultiplyColor = util.premultiply; + var createVertexAddMethodCache = {}; function createVertexAddMethod(programName, programInterface, bufferName) { diff --git a/js/data/bucket/circle_bucket.js b/js/data/bucket/circle_bucket.js index d7b36d84d32..e85c1d129d1 100644 --- a/js/data/bucket/circle_bucket.js +++ b/js/data/bucket/circle_bucket.js @@ -35,6 +35,17 @@ CircleBucket.prototype.programInterfaces = { '(x * 2) + ((extrudeX + 1) / 2)', '(y * 2) + ((extrudeY + 1) / 2)' ] + }, { + name: 'color', + components: 4, + type: 'Uint8', + value: ( + 'this._premultiplyColor(' + + 'this.layer.paint["circle-color"],' + + 'this.layer.paint["circle-opacity"]' + + ')' + ), + multiplier: 255 }] } }; diff --git a/js/render/draw_circle.js b/js/render/draw_circle.js index 92593a7fcce..eae23ce4811 100644 --- a/js/render/draw_circle.js +++ b/js/render/draw_circle.js @@ -1,7 +1,6 @@ 'use strict'; var browser = require('../util/browser'); -var util = require('../util/util'); module.exports = drawCircles; @@ -28,10 +27,6 @@ function drawCircles(painter, source, layer, coords) { gl.uniform1f(program.u_blur, Math.max(layer.paint['circle-blur'], antialias)); gl.uniform1f(program.u_size, layer.paint['circle-radius']); - var color = util.premultiply(layer.paint['circle-color'], layer.paint['circle-opacity']).map(function(v) { return v * 255; }); - gl.disableVertexAttribArray(program.a_color); - gl.vertexAttrib4fv(program.a_color, color); - for (var i = 0; i < coords.length; i++) { var coord = coords[i]; From 6ceed1f4724f75fdeb56b5d2a02c23a9cbba359b Mon Sep 17 00:00:00 2001 From: Lucas Wojciechowski Date: Wed, 27 Jan 2016 09:30:33 -0800 Subject: [PATCH 06/32] Allow attributes to be disabled --- debug/random.geojson | 2 +- debug/site.js | 6 +- js/data/bucket.js | 95 ++++++++++++++++++++++++++++--- js/data/bucket/circle_bucket.js | 35 +++++++----- js/render/draw_circle.js | 3 +- js/render/draw_collision_debug.js | 2 +- js/render/draw_fill.js | 4 +- js/render/draw_line.js | 3 +- js/render/draw_symbol.js | 7 ++- js/style/style_layer.js | 10 ++++ 10 files changed, 134 insertions(+), 33 deletions(-) diff --git a/debug/random.geojson b/debug/random.geojson index 68ebcf05275..8f4e71844ed 100644 --- a/debug/random.geojson +++ b/debug/random.geojson @@ -1 +1 @@ -{"type":"FeatureCollection","features":[{"type":"Feature","geometry":{"type":"Point","coordinates":[149.49637136422098,-9.654809371568263]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-32.20742748118937,18.248684629797935]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-136.7495269048959,-27.38302533980459]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[57.49033374711871,-2.0502609573304653]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-35.09258760139346,-48.48457373678684]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[12.515633599832654,40.50837479531765]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[87.57965345866978,88.35084596648812]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[48.276180010288954,-75.54946155287325]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-155.12388995848596,6.622756691649556]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[94.88623925484717,49.332872168160975]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[60.52322088740766,3.2778834644705057]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-63.66609351709485,53.50058400537819]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[7.593629099428654,-79.76764933671802]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[95.09749154560268,-47.59878045413643]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-29.926261017099023,6.000985521823168]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[24.312616987153888,-10.033922642469406]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[120.91760211624205,-72.98863234464079]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[176.1061437986791,-28.75677346251905]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-103.94753599539399,-66.18871176615357]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-114.15921684354544,43.33588196430355]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[120.14222511090338,65.34176852088422]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[162.69001602195203,-20.188797153532505]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-132.7384481113404,59.35609757434577]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[151.21787237003446,-55.01443739980459]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[48.94104430451989,17.261870596557856]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[110.3044160362333,-9.162420881912112]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[24.650879502296448,37.41224037017673]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[6.55064650811255,-80.02935272175819]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-80.96966198645532,-22.852723225951195]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-5.970262913033366,4.047233420424163]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-56.763304229825735,-39.60214911494404]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-95.64289779402316,35.48857259564102]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[169.02061921544373,58.58780784532428]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-49.70909879542887,-22.62584966607392]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[54.00681495666504,39.8978583002463]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[135.5449115484953,32.358882520347834]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[24.55371782183647,45.599905801936984]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[69.13071971386671,-12.706649419851601]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[26.994442865252495,46.82549917604774]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-162.23010728135705,77.0919276215136]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[14.995029559358954,30.44066105503589]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[128.0795504618436,43.8307428220287]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-104.52400088310242,84.28086372558028]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-83.39116405695677,80.11053497437388]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-169.15256646461785,29.123284625820816]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[135.97556702792645,-15.959288226440549]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-0.24300578981637955,-77.82989162486047]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[38.77691642381251,42.55800019484013]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[95.83638723939657,-78.0720021435991]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[132.05492452718318,-60.289104734547436]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-79.45233182050288,24.808030799031258]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[149.3677013553679,-58.10220593120903]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-94.85853572376072,7.255792384967208]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[158.3035398647189,53.35667263716459]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-110.09574243798852,-43.99398373905569]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-52.67722936347127,27.093252972699702]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[150.15035980381072,-80.53846763446927]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[9.024425586685538,-28.18460740149021]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-51.82950630784035,20.498926313593984]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-13.797060800716281,-42.18382612802088]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-117.65530060976744,-9.724032743833959]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-96.10944810323417,-82.18850092962384]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-165.59358338825405,-74.11594199482352]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-18.176996894180775,21.17788129951805]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-51.399409398436546,39.13681254722178]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[21.84296532534063,-35.34000772051513]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-169.2613579519093,73.16755077335984]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-145.11284035630524,-78.65141767077148]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[125.41096463799477,-78.09826633427292]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-43.089810283854604,-63.84157964028418]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-55.58937284164131,61.76249725744128]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[2.9992335475981236,34.93651384022087]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-179.1684382595122,-36.7301441822201]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[3.758228961378336,-36.35523778852075]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[126.35871229693294,-73.91656078398228]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[90.33874975517392,50.97015139181167]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[123.69971914216876,-40.703937211073935]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[23.663350408896804,-5.27460019569844]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-166.637580441311,-48.44638771377504]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-58.89038298279047,-37.10679828654975]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-34.29882380180061,19.043962117284536]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[71.50328806601465,61.23050490394235]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[5.372699433937669,-13.683151029981673]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[15.534586999565363,63.58376717660576]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-27.496591536328197,80.22686891723424]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[102.24426973611116,2.16921198181808]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[75.82710740156472,-45.28801170643419]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-118.21109589189291,-39.60671909619123]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[46.47027407772839,-63.175592171028256]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-119.10293164663017,54.71257191617042]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[78.5866369586438,14.075388433411717]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-38.26819198206067,-72.82141484320164]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[134.69311656430364,-38.25590373482555]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-177.73576171137393,82.00566588900983]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-55.613233521580696,31.487578321248293]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-168.1243485957384,40.77788412105292]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[154.12042871117592,-20.77773309778422]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-15.718078510835767,20.40679955855012]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-176.5286646410823,-70.40267100557685]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-32.08226814866066,75.64151232596487]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-27.555824434384704,71.51631538756192]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[48.07085330598056,42.78952797409147]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-9.559019934386015,-40.79291358590126]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-85.9208913333714,-51.566796214319766]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[55.81204514019191,-16.223004986532032]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[61.25111969187856,0.04141175653785467]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[132.91573802009225,-78.94584295805544]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-97.36254681833088,-24.4186201505363]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[14.804459111765027,-84.26275152247399]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-59.36579857952893,86.52482149656862]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-143.1278306711465,43.04532102774829]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-87.85176498815417,-4.281637282110751]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[138.59302651137114,-25.457210708409548]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-61.750815231353045,-78.79723655059934]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-131.30710705183446,2.1586280269548297]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-30.80566104501486,89.74685768131167]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-113.42367156408727,-27.952353372238576]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[65.66230519674718,-22.028965833596885]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-92.10858978331089,-38.35110099054873]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-154.14954258129,80.32227404415607]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-19.887921614572406,-64.32063193526119]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[125.35523537546396,-35.98540982231498]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[31.097621507942677,-89.00853195693344]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[14.43086595274508,84.92379216011614]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[37.901069736108184,-22.19181087333709]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[97.93589439243078,-4.197207251563668]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-38.304785611107945,-69.23459123354405]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-173.49190623499453,-61.27010130789131]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[0.9125038515776396,59.265824225731194]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-143.5661626327783,-19.61784667801112]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[140.19756828434765,45.24801858700812]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-146.82779006659985,44.627577625215054]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[128.03192960098386,-43.90957793220878]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-114.60585057735443,-55.70240263827145]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[80.67271177656949,-25.16001457348466]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-37.23169421777129,41.82630505878478]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[14.471303941681981,-67.27960975840688]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[29.351948769763112,-37.61261290870607]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-118.98626661859453,85.98206254653633]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-133.93749067559838,-52.089063758030534]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-49.4992710929364,53.66898624692112]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[114.8944896273315,-31.78370848763734]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[143.98385466076434,61.59199266694486]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-145.0802127085626,-10.938919996842742]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[169.7884490713477,52.00517109129578]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-172.17302148230374,-42.275232123211026]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[125.76004978269339,33.90714329201728]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[114.21696455217898,89.54513410571963]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[159.5182471163571,7.914179898798466]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-160.38636619225144,9.510616078041494]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[109.20965918339789,81.20597960427403]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-25.193190816789865,54.03361869044602]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[151.75552938133478,1.9545546267181635]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-179.74409027025104,84.30114571005106]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-21.349562080577016,86.70269302558154]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-88.04809698835015,1.114083006978035]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-1.8410969339311123,82.13660018518567]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[131.50190021842718,5.552907628007233]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-92.0619879104197,10.845119752921164]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-48.7168724834919,48.143608435057104]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[117.41884041577578,13.289493229240179]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[64.85487101599574,-8.208351884968579]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-77.34714634716511,1.4469616999849677]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-93.29159198328853,-72.4770661862567]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[66.42859940417111,-78.27111136168242]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[96.73809905536473,19.116298486478627]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-61.793883545324206,72.12481695692986]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-98.2477710954845,37.38704076502472]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[148.8963887002319,-50.787345939315856]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[87.8617275506258,-77.5012998143211]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[128.15870479680598,-3.630034038797021]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-131.9270085543394,-68.15944958478212]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[167.64817837625742,82.61665357276797]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[4.287826269865036,-45.000482546165586]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-138.9921465050429,-1.8423309596255422]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-21.255824491381645,15.885210391134024]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-157.9392747581005,75.71163214277476]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-73.2338843587786,-4.1237724758684635]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[104.13818456232548,-36.57757187727839]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-56.15695391781628,69.78384184185416]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[62.47151901014149,5.825139814987779]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[105.97231487743556,-10.9131254022941]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[63.177881855517626,-22.38561765756458]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[178.24590135365725,68.59754270408303]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-34.85676401294768,-30.68933938164264]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-167.50994184985757,64.53059337101877]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-108.98514504544437,17.155385515652597]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-148.04009104147553,-24.630167903378606]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-168.31853373907506,68.53996183723211]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-79.39349262043834,22.610792331397533]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[128.11431717127562,-17.222693203948438]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-95.66886300221086,20.973391821607947]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[59.1321013122797,77.73103675805032]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[64.46210349909961,-3.797665061429143]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-152.65658154152334,61.17343520745635]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-5.032834801822901,-80.51403128542006]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[149.14262588135898,14.811443709768355]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-117.67312103882432,35.601952923461795]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[58.696921449154615,76.93483407609165]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[115.12597859837115,-27.19985560979694]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-87.42650183849037,60.63311350531876]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[38.965260637924075,-12.436161800287664]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-124.12975067272782,46.57280751038343]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-79.01402894407511,59.47802145034075]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[3.1428339891135693,-50.61879039276391]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[150.56984103284776,-67.89576589129865]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[162.56736790761352,-81.48028237279505]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[143.31506738439202,44.763501500710845]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[87.38029123283923,82.75920418091118]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[161.4928848668933,23.620088850148022]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-46.51166198775172,17.211917680688202]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[76.91065756604075,-3.8099643727764487]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[68.9309889357537,-33.26238655485213]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[128.56363772414625,-57.36148780211806]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[1.0216147731989622,-66.06437901500612]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[112.04996204003692,87.48259470798075]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[16.097388714551926,41.1862831749022]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[41.597789945080876,82.33694112394005]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[170.73053332976997,20.212005176581442]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-51.28361273556948,12.821408319287002]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-126.20331206358969,32.627388993278146]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[68.47599014639854,-48.73019208200276]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[7.503653643652797,-61.4725673943758]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-176.48825615644455,-51.726085441187024]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-31.279294649139047,28.34117544349283]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[7.088211653754115,-24.105680999346077]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-33.13733744435012,-7.600142173469067]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[118.55583686381578,-53.2780737709254]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-141.7861074116081,10.011035855859518]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[22.935774559155107,44.0105700166896]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-89.36553846113384,4.018968511372805]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-3.784685432910919,6.479591270908713]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-42.49874101951718,-19.924558368511498]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[86.19898351840675,74.238501470536]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-167.01308970339596,-19.07945587299764]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[7.472661640495062,55.85181953385472]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[10.16387709416449,9.010712709277868]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-111.77245139144361,-63.788525252602994]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[132.7932282909751,59.112535561434925]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[32.11806700564921,42.33946813736111]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[84.23201076686382,74.00251803919673]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-159.94870654307306,83.00069867167622]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-74.33175626210868,40.2495814813301]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-12.03122053295374,53.76494717784226]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[38.51891281083226,60.39851380046457]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-177.92527955956757,-27.078501577489078]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[27.8085361327976,-89.31377589236945]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[31.712982952594757,-62.790777194313705]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-110.4998944234103,39.87759333103895]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[6.665234751999378,74.6124066459015]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[142.03034416772425,44.22287402674556]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-146.78715887479484,85.84780478384346]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-20.139846755191684,-31.440163403749466]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[53.40938273817301,-33.084354805760086]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[168.71282072737813,-71.07424995861948]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-48.08681420981884,36.95648339577019]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-116.24423461966217,-49.2674556048587]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-177.43650136515498,89.10378319211304]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[117.56597299128771,-60.207746461965144]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[47.808708446100354,-62.97403304837644]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[8.761591063812375,87.67553203739226]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[173.6599436122924,-47.94121703132987]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[145.28842580504715,71.71248435974121]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[22.99990575760603,-56.52551684528589]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-113.92999400384724,-6.061111846938729]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-112.70329082384706,40.00028905458748]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-157.0024934783578,-25.5547647876665]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-157.7265284769237,-8.688656771555543]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-65.3096076194197,-16.137895099818707]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-100.8437733259052,33.71473289094865]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-135.32863640226424,15.69878248963505]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-175.34784416668117,-81.23251222539693]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-176.63457275368273,27.68016146030277]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-3.8195206224918365,21.758858459070325]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-7.554967403411865,-87.93070897925645]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-148.34330152720213,15.5256537348032]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-22.70371813327074,-88.98335041478276]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-76.80486177094281,87.56499422248453]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[94.73564176820219,-23.18279926199466]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-122.16504757292569,10.985643323510885]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[22.098287483677268,-74.52641573734581]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[97.66668090596795,41.98179124854505]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[13.54695713147521,-38.86961947660893]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[84.5068723987788,-89.03763505630195]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[105.55773354135454,-21.485011079348624]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-6.420687530189753,28.988473741337657]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-57.10427041165531,-84.32162466924638]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-27.326627410948277,-25.176213565282524]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[50.39919488132,5.478498330339789]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[55.15531028620899,-82.91263449937105]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[17.161540472880006,10.206110067665577]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-6.007457105442882,21.947381501086056]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-22.560217771679163,7.8930790442973375]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[65.83445104770362,-76.19783260859549]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-79.10166140645742,-57.76479174848646]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-52.24335338920355,28.128394335508347]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-167.75034346617758,32.644973010756075]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-60.84821074269712,68.28051251359284]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-124.8287599068135,-4.834600402973592]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[12.214657980948687,-7.677580271847546]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-159.34292058460414,0.939008267596364]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[34.17610913515091,-53.271518871188164]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[35.44008119031787,-45.07839510217309]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-60.85252775810659,46.61097060889006]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-96.09791326336563,33.897040290758014]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[10.84976764395833,-23.576332926750183]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-99.40655621699989,40.975414481945336]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-159.6056765690446,-54.53383868560195]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-111.82251306250691,6.798568242229521]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[51.06023283675313,-32.82271181233227]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-16.16575051099062,-88.20602964609861]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-108.40289345942438,74.52982507646084]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-21.357374098151922,33.79067603498697]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[77.00604454614222,-45.98769270814955]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-153.91111800447106,-53.52022921666503]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-151.2720881961286,-85.24132933001965]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[160.26194446720183,74.5986429322511]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[42.970063062384725,38.725563855841756]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[111.6670954786241,23.52170533966273]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-73.96401124075055,-33.71974874753505]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-13.79294553771615,-7.111376929096878]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-9.01755946688354,53.93363012932241]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[42.33486316166818,-28.878679485060275]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[122.18439082615077,-54.9345934856683]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[71.76295515149832,-70.35853689070791]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-156.8277490697801,-70.29983284883201]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[87.3418350610882,59.3576778145507]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-47.04308554530144,69.09951736684889]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-88.75740104354918,1.9157659448683262]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[147.92443921789527,55.991847859695554]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[128.52962773293257,26.396703282371163]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-94.97377046383917,56.86219673138112]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[99.80871565639973,32.0507845049724]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-67.59248885326087,47.07945814356208]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[146.41556630842388,-3.0061532696709037]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-21.348323486745358,62.35960899386555]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-165.7372110709548,-78.70617899112403]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-87.8734134323895,-58.981731394305825]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[118.48579172976315,-0.04956013988703489]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-23.929348513484,54.68587295617908]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-178.6426289845258,-73.14786633476615]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[30.740530053153634,58.38801684323698]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[144.24143322743475,-74.4166634324938]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-173.21241904981434,-23.111277068965137]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-107.08533606491983,77.8691914351657]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-3.0583822913467884,-8.07470298372209]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[142.1726858150214,66.06418832670897]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-72.12944003753364,1.034755501896143]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-28.326070625334978,44.35426794458181]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-156.58604733645916,9.969834652729332]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-46.83521627448499,-67.70592160988599]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-66.11459956504405,19.916619365103543]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-57.6163783762604,-74.36286498326808]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-21.96260266005993,1.3756098272278905]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-167.64215279370546,-51.04974158573896]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-176.17273692972958,-68.08571871835738]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-14.52274871058762,-31.73920582048595]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[14.299073182046413,-23.6265701148659]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[154.40124157816172,0.8706196490675211]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[132.24152429960668,48.12327150721103]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-174.7616035491228,77.95839596539736]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-49.875975362956524,-82.87523021455854]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-46.84728378430009,-13.968596700578928]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[1.8018254544585943,-77.04069994390011]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[47.43022277019918,69.33753441553563]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[30.34634574316442,84.55966786947101]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[112.62707585468888,-0.7477007573470473]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[38.783310893923044,-65.76383171137422]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[4.004682647064328,54.02404320426285]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[118.39305385015905,78.25564247556031]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[172.90896465070546,-36.173745822161436]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-41.62649025209248,28.12089948914945]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-105.92365097254515,-67.9277648543939]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[66.90812934190035,19.56935254856944]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-168.35342357866466,-40.26683282107115]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[62.108105421066284,82.71303778979927]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-111.95974258705974,50.35987776238471]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[103.6906898394227,53.36193860974163]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[75.43049778789282,-44.569205422885716]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[159.96820813044906,-54.039471773430705]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[45.07046598941088,-85.21366498433053]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[138.77374218776822,-61.99772702995688]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[128.1855802796781,71.63413338828832]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[87.69008344039321,20.79491708893329]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[65.77875749208033,-34.482794841751456]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-159.52017948031425,7.437892626039684]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[25.50886963494122,-48.584610619582236]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-84.51872759498656,-35.723060527816415]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[148.35694441571832,50.37374516017735]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-41.84343854896724,24.71985979937017]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-172.19425158575177,-70.63582307193428]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[77.11480719968677,28.662969111464918]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-98.62663102336228,-17.722317832522094]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-67.42590799927711,-81.14906660746783]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-15.245613427832723,-0.23437766823917627]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[53.54975648224354,20.513179572299123]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[157.46731761842966,-11.566166351549327]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-52.09023529663682,6.282507749274373]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-83.07940570637584,-11.092898910865188]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-34.011048171669245,-63.91121062450111]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-42.468092339113355,31.389638339169323]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-178.01955727860332,11.634456650353968]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-64.60163025185466,-16.182731157168746]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[93.32587975077331,9.498496516607702]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[138.447703756392,63.300821115262806]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[107.98797860741615,64.21587327495217]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[44.89903402514756,11.920283697545528]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[97.09358906373382,66.50570872239769]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[135.55744014680386,-39.8464760184288]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[24.461368694901466,-57.544735819101334]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[86.75982713699341,-37.044484368525445]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[154.97563261538744,46.89816097263247]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[63.62180722877383,48.797338963486254]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-81.6332977451384,-69.5285688014701]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-125.86669416166842,11.151868575252593]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[24.033637242391706,1.986960656940937]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-90.83612258546054,41.83964733034372]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[141.40925436280668,-56.18986068293452]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-74.97196728363633,69.41084241494536]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[86.00875438190997,-37.44377627503127]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[0.16310655511915684,30.11453411076218]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[59.132625265046954,80.28071338310838]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-26.065974170342088,-6.162758809514344]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[157.86116070114076,-55.637453868985176]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[45.68473267368972,-44.569785576313734]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[151.67410547845066,-58.36482801474631]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[90.00907181762159,-75.99146893713623]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-104.12095547653735,-15.194700784049928]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[117.50006223097444,28.45345518551767]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-155.98592184484005,76.06090474873781]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[75.13844445347786,1.7793576791882515]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-145.20926292054355,78.05272841826081]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-128.45609254203737,-6.3851268868893385]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-81.19094272144139,-29.85254536382854]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-165.57452034205198,-38.7693885108456]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[48.1255017220974,-76.26653064507991]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-73.45806020312011,55.7819620706141]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-102.19109049998224,-2.3640777356922626]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-2.548731230199337,-42.93387838639319]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-157.32786169275641,50.21063269581646]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[70.70466636680067,-7.382422029040754]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-102.35983388498425,44.1893474617973]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-79.82309818267822,61.13497647922486]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[49.999863831326365,15.985758653841913]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-24.133144933730364,2.1917387237772346]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[123.59778338111937,-57.69319223240018]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[94.0684381686151,-66.65535859297961]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[157.03033162280917,89.06510858796537]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-114.44523193873465,-3.16591611597687]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-155.92443882487714,79.3146219290793]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-138.58794959262013,-83.43364333268255]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-28.40943267568946,-12.023972407914698]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[153.57163391076028,-14.328520400449634]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-131.6473619453609,-31.539645195007324]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[8.759269108995795,-83.55959844309837]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[75.3498440142721,-65.3848987352103]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-173.8281673192978,-61.68116744142026]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[64.335723426193,63.000016110017896]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-154.10092351958156,20.27713805437088]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[107.18762369826436,-75.27225931640714]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-80.6641783285886,89.01925991289318]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-87.12357725948095,-26.60714873112738]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-62.47553293593228,-43.975366819649935]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-56.76539660431445,-52.99163174349815]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[168.49611894227564,75.842468412593]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[155.85793999023736,58.690635985694826]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[60.55452469736338,-52.98390756826848]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[142.33582170680165,-60.806593000888824]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[32.24343739449978,83.23412289842963]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-128.7367181573063,-44.541783523745835]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[26.811355277895927,-48.45495829358697]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[53.918052446097136,-50.31594221480191]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[46.152025954797864,82.9171155905351]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-148.16632015630603,-80.6247699726373]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[54.16946595534682,89.37623892445117]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[39.428468411788344,71.39732718933374]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-29.818561356514692,-87.24294427782297]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[99.59268576465547,14.529108982533216]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-30.27795326896012,51.272379979491234]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-28.50434297695756,22.147771390154958]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[98.66102700121701,-26.98136743158102]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[18.10657256282866,18.187531353905797]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[88.62086964771152,27.71508181001991]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-165.33975077793002,-74.81050299480557]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[146.61559757776558,-30.2143995417282]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-31.76683575846255,-62.23036696203053]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-171.3705331273377,53.36500286590308]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[99.11290822550654,-42.837288728915155]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[144.2002857942134,35.85235783830285]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-94.01839108206332,-27.737779919989407]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-19.553628768771887,87.03391993883997]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[178.0183859076351,21.385139278136194]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[71.92996750585735,16.57038039062172]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[122.1935795713216,58.11467200051993]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-147.9172930587083,10.932626193389297]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-92.24035447463393,26.318431729450822]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-87.07592094317079,47.604615851305425]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-127.08934234455228,62.0113266678527]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[16.953506162390113,-56.5413715923205]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[2.1253615524619818,-57.9912473866716]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[19.528056671842933,-1.8807247700169683]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[57.66796647571027,41.272982941009104]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[25.238397186622024,21.62111126817763]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[21.714930161833763,65.44598327949643]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-10.444665905088186,-14.791977368295193]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[138.9792958740145,64.79380751028657]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-107.37737297080457,-37.59859719313681]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-15.222228253260255,32.659749845042825]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-136.06521433219314,-49.82728966511786]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-139.78533430024981,-80.49050428904593]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-65.04433865658939,83.0693593248725]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-58.45413375645876,4.437992465682328]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[142.07782488316298,-63.647979008965194]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-77.22275203093886,60.31969440635294]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[118.87979784049094,46.770676667802036]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[15.858520232141018,-18.45026441384107]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[4.697578875347972,-89.0603848407045]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[0.29192584566771984,-39.75866707507521]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-41.20185096748173,-68.3810604410246]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[82.81948900781572,-75.26050860062242]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-45.95500378869474,68.84790445212275]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[36.90243864431977,19.058164414018393]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-126.76482019945979,-44.839791068807244]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-13.311854638159275,36.16773976944387]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-114.62683400139213,-54.12948612123728]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-3.5442145820707083,-11.6369563434273]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-86.40065554529428,-89.81849625706673]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-9.042281722649932,-40.06876312661916]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[133.63959029316902,54.72414292395115]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-169.43282327614725,-17.5564864045009]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[175.429325401783,63.34874477237463]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[24.69895239919424,-30.67570684477687]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-9.021967090666294,-2.826137817464769]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[80.16331896185875,2.071308419108391]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[156.4887184370309,-0.4145095031708479]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[56.957034738734365,-2.7943947119638324]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[122.7041953895241,40.22421536967158]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-140.48747568391263,50.707125761546195]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[143.19000905379653,7.287166477181017]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-10.800317181274295,-53.24537890031934]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-153.7102773785591,-48.76605811994523]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[80.65161150880158,-43.97842189762741]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[142.3912587761879,83.58667865395546]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[150.17928080633283,6.153912632726133]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-64.38352927565575,9.823483773507178]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[58.912744307890534,47.091200393624604]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-136.8062863033265,18.77691979520023]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[26.39245541766286,-65.10943888686597]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[130.17711753956974,-70.88348852936178]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[127.53033262677491,36.75111402757466]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-35.52253481000662,7.647080873139203]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[128.69959722273052,-51.24652113765478]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-120.66181140951812,74.7534735314548]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-174.26890907809138,-77.67188294325024]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-48.27908635139465,78.52658380754292]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-116.65499053895473,-21.797942728735507]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[8.694093273952603,36.89580378122628]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-148.6999091785401,78.27982326038182]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-178.54933228343725,66.6313630901277]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-3.974916497245431,67.34660467598587]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-37.00837626121938,-84.84913899097592]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[145.97335637547076,34.95663187466562]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-56.28863143734634,-34.78939819615334]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[84.26847699098289,43.814452551305294]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[0.9951109439134598,-58.606802369467914]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[114.47845210321248,-47.21297385636717]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[88.57873197644949,18.16271819639951]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-75.12000326067209,-4.942738856188953]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[71.3886886741966,80.58788360562176]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[59.52814548276365,-72.08766542840749]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-37.485461784526706,9.892063331790268]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[55.82209210842848,-78.70944311376661]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-126.47003570571542,67.87966396193951]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-38.11043333262205,-42.36012026667595]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-68.53293872438371,33.946018600836396]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[25.31429372727871,2.216475773602724]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-39.22215071506798,11.108307824470103]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-117.8312218375504,19.109560777433217]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-62.75334340520203,-20.642561917193234]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[162.2659510653466,51.33885647635907]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-85.78651492483914,-31.16941594518721]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-107.61159779503942,-62.45351070072502]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-43.02660595625639,2.7774219028651714]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-1.0773403476923704,38.77470628358424]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[106.1652319226414,-45.1364218024537]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[10.962674310430884,58.15609788056463]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-113.2804127689451,8.134800517000258]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-126.32169186137617,7.1781254187226295]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-141.00995030254126,83.41457965783775]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-32.186284800991416,64.95155287440866]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-93.25948778539896,66.35963486507535]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[162.5218812469393,-85.55437384173274]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-35.17443101853132,75.40759117808193]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[79.57131528295577,3.550692368298769]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[158.92351563088596,-36.40004706569016]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[3.597600096836686,78.08964542113245]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[137.93049540370703,44.72675796132535]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-139.01084736920893,2.4230836518108845]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-16.42126075923443,82.77291106525809]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-85.56778874248266,-45.06074319127947]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[147.66723959706724,74.23324228730053]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[9.374368200078607,-12.979612885974348]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[153.0515046324581,-52.29332311078906]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[162.6496712397784,-44.30713335983455]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[100.44096311554313,15.496364515274763]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[122.89768223650753,1.9159417133778334]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-153.06688802316785,-51.16702142637223]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-168.30253830179572,31.704005850479007]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[170.20371629856527,48.0165243241936]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-118.86547593399882,-45.99883854389191]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[109.96864805929363,-28.284501330927014]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-137.61558218859136,-50.10698384139687]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[61.364564979448915,-35.452584475278854]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[46.31492421962321,-27.288771467283368]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-78.66735175251961,10.934291509911418]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[27.444263231009245,28.857922833412886]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[174.26482893526554,56.37111263349652]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-52.15299094095826,-37.03383679501712]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[157.7483246102929,11.989140948280692]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[117.9758988507092,32.22611380275339]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-118.42702779918909,4.662831611931324]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[76.23662388883531,-60.47018527518958]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[120.13247075490654,-63.97725821938366]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[111.46802946925163,-11.89390660263598]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-129.72748928703368,24.851180207915604]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-58.6280823033303,52.36793629825115]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[53.84586028754711,45.34744279459119]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-157.346476810053,54.07337267417461]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[80.17869346775115,-11.497598988935351]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-84.22597562894225,-15.214260583743453]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[140.411637397483,23.820633427239954]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[75.10164597071707,-4.103507841937244]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[128.12896228395402,-54.10661534406245]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[147.15162036940455,34.01164932176471]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-25.507478322833776,60.1441810419783]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-170.80214923247695,87.06924199126661]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-99.63398943655193,-5.895955748856068]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[153.87531998567283,-82.07382136490196]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[59.5482088252902,83.31037675961852]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[169.42099289037287,-44.332350650802255]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-81.0585052985698,84.089150656946]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[96.7931511439383,26.270965849980712]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-41.14514362066984,-88.08813147246838]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-119.17482473887503,-64.68080281745642]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-91.66118399240077,55.00636550132185]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[26.533783692866564,-10.111068841069937]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-169.00810243561864,-80.94805620610714]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[83.92337643541396,-70.91858737170696]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-110.2635882049799,35.70818206295371]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-84.9253267608583,53.84632766246796]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[49.118056604638696,33.63964528776705]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-24.056128319352865,-10.252402815967798]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[97.68289796076715,30.469459891319275]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[74.90200244821608,-9.028823194094002]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[152.8573114424944,-57.970860209316015]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[44.18907961808145,-16.43768711015582]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[148.63756307400763,-84.64647400192916]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[65.60330590233207,-49.905973859131336]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-114.47866693139076,16.95708565413952]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[82.93869199231267,-27.325535165145993]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[66.65229806676507,12.038620957173407]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[99.35018842108548,-6.0751106310635805]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[164.87748512998223,5.028611663728952]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-174.6840535942465,48.577332738786936]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[101.79773608222604,17.904092436656356]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-146.83340367861092,-13.186326962895691]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[141.25340058468282,-36.80527599994093]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-97.06560029648244,-2.499908823519945]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[20.051825875416398,-28.331027599051595]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-69.09159089438617,8.838886083103716]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[122.78145289979875,26.103684855625033]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[63.50223418325186,69.0458416286856]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[94.53597494401038,-85.31247370876372]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[139.0061789844185,-63.92883948516101]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[123.51005268283188,20.858707227744162]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-135.57148729451,-86.3604017905891]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-8.634117403998971,-54.98179650865495]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[128.89058919623494,17.69125864841044]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[51.721485536545515,52.81110931187868]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[45.243595289066434,-69.397656256333]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[47.58692949078977,36.74179695546627]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[1.0598024725914001,-48.50758038461208]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-160.79553843475878,59.84347100369632]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[10.428292946889997,79.16157390922308]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[46.70798409730196,-31.963963955640793]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[80.49403206445277,-54.75363548845053]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-155.00173721462488,36.564332325942814]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-99.90659466013312,-9.391259956173599]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[174.40395344048738,-89.39820596482605]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-58.71011330746114,-12.310519167222083]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[45.58134910650551,-86.06798916589469]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-118.20363834500313,-0.7904840866103768]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-91.22555846348405,77.6962536200881]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[157.6427268795669,-59.78692471515387]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-63.40021602809429,-24.8575499933213]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[164.02178032323718,-89.58281000610441]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-47.211816776543856,-88.26625022571534]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-165.34501519985497,22.663344140164554]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-148.6946810502559,74.30929938331246]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-142.7136817574501,23.76247123349458]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[48.25156210921705,15.501244836486876]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-151.281230840832,63.81557922810316]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[77.62720978818834,53.6626740032807]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-150.78787939622998,41.213500681333244]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-81.1691730748862,4.174361410550773]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-140.40619930252433,23.28936891630292]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[64.12042116746306,-70.6528618466109]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-99.25460337661207,-6.878957943990827]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-140.83626553416252,65.52241249009967]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[134.3968651536852,-9.85090454109013]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[36.84801260009408,35.2313032373786]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-129.0603198017925,40.963220740668476]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[150.62101397663355,49.33374434709549]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[76.06144060380757,-34.03870543465018]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[163.979467805475,46.35060925036669]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[150.81318302080035,7.42405473254621]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-29.844722198322415,3.979192692786455]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-52.835115334019065,25.010058763436973]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-75.71951259858906,-73.55637003667653]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-136.67117664590478,-70.3429255541414]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-73.06231426075101,70.98984100855887]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-156.91963417455554,15.910866516642272]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-12.372664399445057,-4.694070084951818]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[122.77768355794251,-27.85627781879157]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[44.70168672502041,-20.528464056551456]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-12.335470709949732,-23.537044641561806]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-140.60228219255805,58.972157374955714]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-123.32070540636778,-65.65118345897645]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[114.25051387399435,2.9149874160066247]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[21.60031191073358,86.2766702240333]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-154.44363280199468,-24.90678763948381]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[131.86044082045555,-2.055057669058442]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[144.8912670928985,70.94839916098863]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-161.79878384806216,88.69376035407186]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-74.18066591955721,-43.07885346002877]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-97.25652420893312,39.15966148953885]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[92.06954025663435,51.810072655789554]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-120.58100902475417,86.1243249848485]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-55.17589934170246,87.69860942848027]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-20.77899729833007,87.23163298331201]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-155.208152057603,19.185247686691582]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-142.62498152442276,-88.59571723267436]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[2.8595207165926695,-1.6956494143232703]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-61.74546221271157,-21.984181911684573]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[38.3966522756964,-35.05400796420872]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-165.4841711372137,42.794102230109274]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[31.27043003216386,48.79290644545108]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[68.59418503940105,-20.0829499354586]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[155.70995614863932,-63.138474840670824]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[136.2470793351531,-52.26797728333622]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-18.647782830521464,81.263442998752]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-112.93791395612061,-63.559550140053034]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-176.27190364524722,-74.96294588316232]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-77.52021881751716,-3.193904464133084]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-69.39737948589027,85.30365280341357]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-73.1302220094949,52.12994514964521]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-42.95680331066251,-83.01427349913865]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-124.12824913859367,77.08044554572552]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-15.888626771047711,-68.14556093886495]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-18.11258959583938,85.1007583597675]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[73.30335047096014,-18.588879550807178]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-58.48652151413262,34.96230235788971]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[87.45559391565621,12.473756019026041]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[124.65132314711809,41.32522470317781]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[151.07140984386206,24.874209025874734]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[102.68346453085542,-8.390642311424017]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[148.34359036758542,-42.38129588775337]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-99.5635505206883,70.45818820595741]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-96.98967810720205,10.043996097519994]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[159.94566743262112,-59.565283516421914]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[47.37261042930186,62.13936241809279]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[103.2947247941047,43.08889305219054]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[146.88465625047684,-58.113972656428814]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-137.05790324136615,20.781153165735304]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-138.77296476624906,44.03101716656238]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-51.44303695298731,4.003277798183262]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[124.53414648771286,14.820579690858722]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-83.0254449415952,-46.82651187758893]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[46.23384213075042,-63.285608841106296]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-174.23582612536848,4.970098645426333]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[142.85280626267195,-6.949295564554632]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-163.1239470653236,31.5959233045578]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[116.5824761800468,-59.94564942084253]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-136.6917859017849,-37.11364797782153]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[103.25530093163252,24.097403325140476]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[122.77328565716743,-19.82821409124881]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[45.46220228075981,-62.11868908256292]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[5.600327868014574,29.645729823969305]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[176.25345189124346,44.75449895951897]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[155.4346466716379,-34.477172805927694]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[66.23376390896738,-24.263595174998045]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-78.48930604755878,-2.1970386430621147]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[7.033643200993538,5.49671224784106]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[25.701569924131036,87.46262247674167]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[9.385570446029305,-88.91452769748867]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-78.43776396475732,71.47421572823077]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-132.13995876722038,-24.565173578448594]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[85.15281915664673,26.98189042042941]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[33.14551834948361,58.590917866677046]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[12.777206981554627,-40.59781854506582]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-152.95283705927432,40.74522692710161]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[33.83132725954056,35.963861416094005]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[30.65096413716674,-44.38926624599844]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-67.66422185115516,-86.67946358677]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[146.24046784825623,-26.32830686867237]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[161.002056626603,26.446620197966695]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-79.14230517111719,-20.463960911147296]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-157.82514919526875,-10.657458454370499]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-35.3489267360419,-69.7335834056139]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-81.21029569767416,-18.07863819412887]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-151.52670501731336,-44.022110723890364]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[12.007898222655058,54.24188781995326]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[130.3710228111595,-81.0157336620614]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-81.64349206723273,-26.099831108003855]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[148.72055573388934,-54.5143882278353]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-43.217031145468354,48.402493530884385]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-165.64904686063528,0.4130655527114868]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-59.510142747312784,65.23696623276919]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-101.7116868775338,46.655783825553954]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[58.53164926171303,14.368595536798239]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-103.56381808407605,39.015411073341966]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-89.27093988284469,-44.17215290945023]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-91.35125365108252,41.45888156723231]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-131.08146680518985,-66.59091998822987]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-117.08627759478986,-16.947918450459838]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[153.62486980855465,-53.81671339273453]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-106.19803701527417,-80.1888371212408]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-174.25188459455967,29.692986072041094]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[32.50364159233868,-2.496404224075377]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-103.06340957991779,-78.55048308614641]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-108.9317402523011,-60.855275806970894]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[55.77836690470576,40.05867382977158]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-162.11817325092852,49.16000903584063]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[145.13850834220648,3.7004789896309376]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-62.081177132204175,-21.964824576862156]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[20.93774601817131,-53.444322030991316]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-171.9756575860083,-64.89817209541798]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-116.13323274068534,26.10062365885824]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[156.13474253565073,34.234340228140354]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-172.49762929044664,51.66065710131079]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[145.3119023423642,-9.575124210678041]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-2.450918024405837,88.25738921295851]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-149.6307476889342,20.590436118654907]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[31.362647144123912,-4.1990955686196685]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-56.86572044156492,84.52292944304645]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[123.03662016056478,66.05640883091837]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-18.58963832259178,76.13934129942209]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[144.3702707067132,4.861797667108476]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-82.99937093630433,21.335826530121267]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-7.625324930995703,-20.497307181358337]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[97.59205766022205,-55.40064494591206]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[21.26893705688417,68.53056010790169]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[32.33690533787012,72.49751069582999]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-100.7882856298238,70.50066102761775]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-31.679830932989717,-1.3257330190390348]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[159.46228014305234,20.694489609450102]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-28.144683223217726,86.04833926074207]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[140.42864402756095,22.618745206855237]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[130.53889791481197,-24.182720789685845]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[70.73590253479779,-86.99330936651677]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-98.10030215419829,88.10556780081242]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[62.98887446522713,48.63231299445033]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[15.595593843609095,-36.780918440781534]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[51.286226296797395,32.74736411869526]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[114.15404026396573,77.14950484223664]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[39.96729226782918,-53.55903407558799]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-62.48722493648529,28.135843332856894]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[30.14424548484385,16.105412081815302]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-33.47911214455962,-48.06516291573644]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-109.41398383118212,31.07513818424195]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-119.61529349908233,-86.51946256868541]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-66.0242850612849,-82.31715480331331]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[165.09211147204041,-38.887647399678826]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[144.1246804408729,-75.494344253093]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[79.13097124546766,86.41044996678829]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[100.04751775413752,88.57521191239357]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[2.6331742107868195,-30.016327667981386]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-44.79260975494981,51.192964632064104]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-63.4757920447737,23.448330452665687]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[147.41040794178843,74.48687079828233]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-116.04864517226815,-82.21157119609416]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[5.459511307999492,-20.853945594280958]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[12.284392565488815,-79.69690054189414]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-173.80448911339045,-44.92031320463866]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-57.34907361678779,-23.40254321694374]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[85.30275883153081,-57.6118979556486]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-145.31552751548588,7.91812433861196]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-145.0054969266057,-43.26822588220239]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[157.0090924669057,74.83082466758788]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-65.92883471399546,-33.93926358316094]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-121.67479659430683,-25.25705936830491]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-6.830966686829925,43.81351998075843]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-24.402449699118733,70.72312654461712]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-159.88104346208274,-35.65545494668186]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[116.76082606427372,87.32783841434866]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[156.32297242060304,-42.29135069064796]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-24.906591586768627,-70.6086895102635]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[41.94615198299289,23.215975589118898]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-75.20145440474153,-36.35918679647148]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[3.9644900802522898,20.716046816669405]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[11.512293219566345,55.34615373238921]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[161.62389552220702,-39.50468803290278]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-121.57353406772017,26.769525934942067]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[166.50501578114927,-70.0815476803109]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-72.37302795052528,44.60104311816394]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-134.97790245339274,-9.214969296008348]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[108.72965667396784,-20.58310580905527]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[157.98479829914868,14.673859756439924]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-94.03324767015874,-4.2099331598728895]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-133.3403992652893,-6.713104136288166]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[112.25320626050234,-19.00266712065786]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[15.881055146455765,34.15173502173275]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[17.096276711672544,-19.413785682991147]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[52.863189931958914,41.629943093284965]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[12.475525019690394,74.3149668071419]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[102.97935325652361,15.563243804499507]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[12.641265923157334,-47.30335277505219]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-12.239387277513742,-77.80029118526727]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-40.80227231606841,-15.242899786680937]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-131.24569326639175,31.949481619521976]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[114.0491375606507,23.350589415058494]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-176.64725842885673,-43.534041051752865]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[131.65887675248086,-64.21674298122525]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[21.92272173240781,-81.51891858782619]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[138.82551628164947,80.91470603831112]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[143.14156810753047,-25.795541242696345]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-166.800395892933,60.18528484739363]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-144.54735475592315,-3.065854632295668]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-150.76181737706065,-83.66180477198213]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-83.05852730758488,-15.419311146251857]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-87.13011137209833,74.085004767403]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-76.42930540256202,-54.16420857422054]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[142.2087097261101,44.53137613367289]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-64.55603831447661,34.49843735899776]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[42.12144163437188,-11.64163616951555]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[85.58279578574002,-14.729487095028162]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-45.583537705242634,28.341183657757938]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[3.103248104453087,79.1934456769377]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[169.1800141800195,86.2348373606801]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-178.94530529156327,55.29307767748833]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[1.3650418817996979,67.20791833475232]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-114.72778706811368,-48.864370845258236]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[178.343421863392,64.60342163220048]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[177.611748771742,81.19969087187201]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[52.74806583300233,-84.27109310869128]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[10.081364549696445,-62.88397922180593]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[70.66772446967661,-30.233216201886535]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-35.57482288219035,27.087600510567427]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-45.83435098640621,-58.47435358911753]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-20.12691012583673,78.88310491573066]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-19.84980046749115,-73.40256605297327]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[99.99531182460487,82.13316347915679]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-40.85276967845857,67.46957691852003]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[49.27813838236034,52.89751499891281]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[16.61237770691514,69.2438368499279]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[108.57693596743047,36.01974913850427]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-100.97691575065255,-76.26019690185785]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[85.72310507297516,12.150512491352856]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[159.7318111360073,-36.321309520863]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-65.63492445275187,75.05002991762012]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-118.2563142478466,-47.561271605081856]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-124.13570182397962,-13.409972484223545]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-163.29969746991992,43.76724378671497]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[25.569858038797975,-26.38374963775277]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[179.44557675160468,78.71839796192944]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[133.20120753720403,32.78069098480046]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[1.894548749551177,19.006769182160497]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-143.3043299149722,38.19644402246922]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[127.04680577851832,-29.405142883770168]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-46.50432136841118,-77.19790186733007]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[72.4947122298181,-61.52153761591762]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-165.14324208721519,87.08859681151807]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-115.42123725637794,-67.04487129114568]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-114.85377822071314,19.37291443347931]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-145.76720614917576,3.752908087335527]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-36.03865125216544,-39.25180781632662]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[57.959803557023406,-9.167421525344253]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[169.3040323164314,85.74065003078431]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[113.3064625505358,52.742472756654024]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[73.00019471906126,-87.96791079919785]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-91.15831162780523,-14.161390447989106]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[62.7123543061316,30.70386136416346]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-42.837291955947876,-64.84413576778024]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[27.029835870489478,-26.582003817893565]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[152.738849921152,-36.81975812651217]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[124.25105608999729,-36.163670984096825]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-13.68607610464096,45.78369679860771]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-56.30010978318751,47.8517619241029]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-84.61627434007823,37.964905430562794]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-8.222548104822636,-36.19494575075805]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-175.1437535416335,11.454095025546849]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-108.27845404855907,-3.3510214369744062]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-139.8142277263105,41.52774778660387]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-155.18113357946277,77.3847569944337]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-143.1226884573698,-89.31461127474904]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-15.941828219220042,-76.42533141653985]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-153.1626615766436,-76.40048192348331]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-8.032905869185925,18.317440166138113]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-140.5970814730972,-59.16737722232938]},"properties":{}},{"type":"Feature","geometry":{"type":"Point","coordinates":[118.17669169977307,69.35299408156425]},"properties":{}}]} +{"type":"FeatureCollection","features":[{"type":"Feature","geometry":{"type":"Point","coordinates":[149.49637136422098,-9.654809371568263]},"properties":{"mapbox": 13}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-32.20742748118937,18.248684629797935]},"properties":{"mapbox": 38}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-136.7495269048959,-27.38302533980459]},"properties":{"mapbox": 33}},{"type":"Feature","geometry":{"type":"Point","coordinates":[57.49033374711871,-2.0502609573304653]},"properties":{"mapbox": 26}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-35.09258760139346,-48.48457373678684]},"properties":{"mapbox": 76}},{"type":"Feature","geometry":{"type":"Point","coordinates":[12.515633599832654,40.50837479531765]},"properties":{"mapbox": 18}},{"type":"Feature","geometry":{"type":"Point","coordinates":[87.57965345866978,88.35084596648812]},"properties":{"mapbox": 68}},{"type":"Feature","geometry":{"type":"Point","coordinates":[48.276180010288954,-75.54946155287325]},"properties":{"mapbox": 8}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-155.12388995848596,6.622756691649556]},"properties":{"mapbox": 31}},{"type":"Feature","geometry":{"type":"Point","coordinates":[94.88623925484717,49.332872168160975]},"properties":{"mapbox": 49}},{"type":"Feature","geometry":{"type":"Point","coordinates":[60.52322088740766,3.2778834644705057]},"properties":{"mapbox": 17}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-63.66609351709485,53.50058400537819]},"properties":{"mapbox": 71}},{"type":"Feature","geometry":{"type":"Point","coordinates":[7.593629099428654,-79.76764933671802]},"properties":{"mapbox": 44}},{"type":"Feature","geometry":{"type":"Point","coordinates":[95.09749154560268,-47.59878045413643]},"properties":{"mapbox": 89}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-29.926261017099023,6.000985521823168]},"properties":{"mapbox": 60}},{"type":"Feature","geometry":{"type":"Point","coordinates":[24.312616987153888,-10.033922642469406]},"properties":{"mapbox": 51}},{"type":"Feature","geometry":{"type":"Point","coordinates":[120.91760211624205,-72.98863234464079]},"properties":{"mapbox": 94}},{"type":"Feature","geometry":{"type":"Point","coordinates":[176.1061437986791,-28.75677346251905]},"properties":{"mapbox": 87}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-103.94753599539399,-66.18871176615357]},"properties":{"mapbox": 34}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-114.15921684354544,43.33588196430355]},"properties":{"mapbox": 29}},{"type":"Feature","geometry":{"type":"Point","coordinates":[120.14222511090338,65.34176852088422]},"properties":{"mapbox": 16}},{"type":"Feature","geometry":{"type":"Point","coordinates":[162.69001602195203,-20.188797153532505]},"properties":{"mapbox": 53}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-132.7384481113404,59.35609757434577]},"properties":{"mapbox": 20}},{"type":"Feature","geometry":{"type":"Point","coordinates":[151.21787237003446,-55.01443739980459]},"properties":{"mapbox": 67}},{"type":"Feature","geometry":{"type":"Point","coordinates":[48.94104430451989,17.261870596557856]},"properties":{"mapbox": 51}},{"type":"Feature","geometry":{"type":"Point","coordinates":[110.3044160362333,-9.162420881912112]},"properties":{"mapbox": 54}},{"type":"Feature","geometry":{"type":"Point","coordinates":[24.650879502296448,37.41224037017673]},"properties":{"mapbox": 68}},{"type":"Feature","geometry":{"type":"Point","coordinates":[6.55064650811255,-80.02935272175819]},"properties":{"mapbox": 48}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-80.96966198645532,-22.852723225951195]},"properties":{"mapbox": 51}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-5.970262913033366,4.047233420424163]},"properties":{"mapbox": 78}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-56.763304229825735,-39.60214911494404]},"properties":{"mapbox": 73}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-95.64289779402316,35.48857259564102]},"properties":{"mapbox": 73}},{"type":"Feature","geometry":{"type":"Point","coordinates":[169.02061921544373,58.58780784532428]},"properties":{"mapbox": 1}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-49.70909879542887,-22.62584966607392]},"properties":{"mapbox": 61}},{"type":"Feature","geometry":{"type":"Point","coordinates":[54.00681495666504,39.8978583002463]},"properties":{"mapbox": 20}},{"type":"Feature","geometry":{"type":"Point","coordinates":[135.5449115484953,32.358882520347834]},"properties":{"mapbox": 54}},{"type":"Feature","geometry":{"type":"Point","coordinates":[24.55371782183647,45.599905801936984]},"properties":{"mapbox": 31}},{"type":"Feature","geometry":{"type":"Point","coordinates":[69.13071971386671,-12.706649419851601]},"properties":{"mapbox": 45}},{"type":"Feature","geometry":{"type":"Point","coordinates":[26.994442865252495,46.82549917604774]},"properties":{"mapbox": 81}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-162.23010728135705,77.0919276215136]},"properties":{"mapbox": 2}},{"type":"Feature","geometry":{"type":"Point","coordinates":[14.995029559358954,30.44066105503589]},"properties":{"mapbox": 5}},{"type":"Feature","geometry":{"type":"Point","coordinates":[128.0795504618436,43.8307428220287]},"properties":{"mapbox": 1}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-104.52400088310242,84.28086372558028]},"properties":{"mapbox": 48}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-83.39116405695677,80.11053497437388]},"properties":{"mapbox": 76}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-169.15256646461785,29.123284625820816]},"properties":{"mapbox": 53}},{"type":"Feature","geometry":{"type":"Point","coordinates":[135.97556702792645,-15.959288226440549]},"properties":{"mapbox": 59}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-0.24300578981637955,-77.82989162486047]},"properties":{"mapbox": 70}},{"type":"Feature","geometry":{"type":"Point","coordinates":[38.77691642381251,42.55800019484013]},"properties":{"mapbox": 49}},{"type":"Feature","geometry":{"type":"Point","coordinates":[95.83638723939657,-78.0720021435991]},"properties":{"mapbox": 31}},{"type":"Feature","geometry":{"type":"Point","coordinates":[132.05492452718318,-60.289104734547436]},"properties":{"mapbox": 63}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-79.45233182050288,24.808030799031258]},"properties":{"mapbox": 51}},{"type":"Feature","geometry":{"type":"Point","coordinates":[149.3677013553679,-58.10220593120903]},"properties":{"mapbox": 33}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-94.85853572376072,7.255792384967208]},"properties":{"mapbox": 94}},{"type":"Feature","geometry":{"type":"Point","coordinates":[158.3035398647189,53.35667263716459]},"properties":{"mapbox": 73}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-110.09574243798852,-43.99398373905569]},"properties":{"mapbox": 67}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-52.67722936347127,27.093252972699702]},"properties":{"mapbox": 64}},{"type":"Feature","geometry":{"type":"Point","coordinates":[150.15035980381072,-80.53846763446927]},"properties":{"mapbox": 83}},{"type":"Feature","geometry":{"type":"Point","coordinates":[9.024425586685538,-28.18460740149021]},"properties":{"mapbox": 96}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-51.82950630784035,20.498926313593984]},"properties":{"mapbox": 19}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-13.797060800716281,-42.18382612802088]},"properties":{"mapbox": 18}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-117.65530060976744,-9.724032743833959]},"properties":{"mapbox": 22}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-96.10944810323417,-82.18850092962384]},"properties":{"mapbox": 38}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-165.59358338825405,-74.11594199482352]},"properties":{"mapbox": 65}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-18.176996894180775,21.17788129951805]},"properties":{"mapbox": 98}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-51.399409398436546,39.13681254722178]},"properties":{"mapbox": 65}},{"type":"Feature","geometry":{"type":"Point","coordinates":[21.84296532534063,-35.34000772051513]},"properties":{"mapbox": 33}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-169.2613579519093,73.16755077335984]},"properties":{"mapbox": 64}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-145.11284035630524,-78.65141767077148]},"properties":{"mapbox": 29}},{"type":"Feature","geometry":{"type":"Point","coordinates":[125.41096463799477,-78.09826633427292]},"properties":{"mapbox": 41}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-43.089810283854604,-63.84157964028418]},"properties":{"mapbox": 81}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-55.58937284164131,61.76249725744128]},"properties":{"mapbox": 57}},{"type":"Feature","geometry":{"type":"Point","coordinates":[2.9992335475981236,34.93651384022087]},"properties":{"mapbox": 74}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-179.1684382595122,-36.7301441822201]},"properties":{"mapbox": 72}},{"type":"Feature","geometry":{"type":"Point","coordinates":[3.758228961378336,-36.35523778852075]},"properties":{"mapbox": 22}},{"type":"Feature","geometry":{"type":"Point","coordinates":[126.35871229693294,-73.91656078398228]},"properties":{"mapbox": 14}},{"type":"Feature","geometry":{"type":"Point","coordinates":[90.33874975517392,50.97015139181167]},"properties":{"mapbox": 68}},{"type":"Feature","geometry":{"type":"Point","coordinates":[123.69971914216876,-40.703937211073935]},"properties":{"mapbox": 44}},{"type":"Feature","geometry":{"type":"Point","coordinates":[23.663350408896804,-5.27460019569844]},"properties":{"mapbox": 42}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-166.637580441311,-48.44638771377504]},"properties":{"mapbox": 4}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-58.89038298279047,-37.10679828654975]},"properties":{"mapbox": 33}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-34.29882380180061,19.043962117284536]},"properties":{"mapbox": 73}},{"type":"Feature","geometry":{"type":"Point","coordinates":[71.50328806601465,61.23050490394235]},"properties":{"mapbox": 64}},{"type":"Feature","geometry":{"type":"Point","coordinates":[5.372699433937669,-13.683151029981673]},"properties":{"mapbox": 83}},{"type":"Feature","geometry":{"type":"Point","coordinates":[15.534586999565363,63.58376717660576]},"properties":{"mapbox": 23}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-27.496591536328197,80.22686891723424]},"properties":{"mapbox": 50}},{"type":"Feature","geometry":{"type":"Point","coordinates":[102.24426973611116,2.16921198181808]},"properties":{"mapbox": 33}},{"type":"Feature","geometry":{"type":"Point","coordinates":[75.82710740156472,-45.28801170643419]},"properties":{"mapbox": 83}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-118.21109589189291,-39.60671909619123]},"properties":{"mapbox": 61}},{"type":"Feature","geometry":{"type":"Point","coordinates":[46.47027407772839,-63.175592171028256]},"properties":{"mapbox": 1}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-119.10293164663017,54.71257191617042]},"properties":{"mapbox": 49}},{"type":"Feature","geometry":{"type":"Point","coordinates":[78.5866369586438,14.075388433411717]},"properties":{"mapbox": 89}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-38.26819198206067,-72.82141484320164]},"properties":{"mapbox": 29}},{"type":"Feature","geometry":{"type":"Point","coordinates":[134.69311656430364,-38.25590373482555]},"properties":{"mapbox": 27}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-177.73576171137393,82.00566588900983]},"properties":{"mapbox": 27}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-55.613233521580696,31.487578321248293]},"properties":{"mapbox": 55}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-168.1243485957384,40.77788412105292]},"properties":{"mapbox": 15}},{"type":"Feature","geometry":{"type":"Point","coordinates":[154.12042871117592,-20.77773309778422]},"properties":{"mapbox": 37}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-15.718078510835767,20.40679955855012]},"properties":{"mapbox": 59}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-176.5286646410823,-70.40267100557685]},"properties":{"mapbox": 28}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-32.08226814866066,75.64151232596487]},"properties":{"mapbox": 63}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-27.555824434384704,71.51631538756192]},"properties":{"mapbox": 42}},{"type":"Feature","geometry":{"type":"Point","coordinates":[48.07085330598056,42.78952797409147]},"properties":{"mapbox": 86}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-9.559019934386015,-40.79291358590126]},"properties":{"mapbox": 77}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-85.9208913333714,-51.566796214319766]},"properties":{"mapbox": 7}},{"type":"Feature","geometry":{"type":"Point","coordinates":[55.81204514019191,-16.223004986532032]},"properties":{"mapbox": 72}},{"type":"Feature","geometry":{"type":"Point","coordinates":[61.25111969187856,0.04141175653785467]},"properties":{"mapbox": 97}},{"type":"Feature","geometry":{"type":"Point","coordinates":[132.91573802009225,-78.94584295805544]},"properties":{"mapbox": 36}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-97.36254681833088,-24.4186201505363]},"properties":{"mapbox": 46}},{"type":"Feature","geometry":{"type":"Point","coordinates":[14.804459111765027,-84.26275152247399]},"properties":{"mapbox": 63}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-59.36579857952893,86.52482149656862]},"properties":{"mapbox": 5}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-143.1278306711465,43.04532102774829]},"properties":{"mapbox": 20}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-87.85176498815417,-4.281637282110751]},"properties":{"mapbox": 71}},{"type":"Feature","geometry":{"type":"Point","coordinates":[138.59302651137114,-25.457210708409548]},"properties":{"mapbox": 88}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-61.750815231353045,-78.79723655059934]},"properties":{"mapbox": 90}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-131.30710705183446,2.1586280269548297]},"properties":{"mapbox": 79}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-30.80566104501486,89.74685768131167]},"properties":{"mapbox": 79}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-113.42367156408727,-27.952353372238576]},"properties":{"mapbox": 84}},{"type":"Feature","geometry":{"type":"Point","coordinates":[65.66230519674718,-22.028965833596885]},"properties":{"mapbox": 37}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-92.10858978331089,-38.35110099054873]},"properties":{"mapbox": 7}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-154.14954258129,80.32227404415607]},"properties":{"mapbox": 11}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-19.887921614572406,-64.32063193526119]},"properties":{"mapbox": 28}},{"type":"Feature","geometry":{"type":"Point","coordinates":[125.35523537546396,-35.98540982231498]},"properties":{"mapbox": 20}},{"type":"Feature","geometry":{"type":"Point","coordinates":[31.097621507942677,-89.00853195693344]},"properties":{"mapbox": 20}},{"type":"Feature","geometry":{"type":"Point","coordinates":[14.43086595274508,84.92379216011614]},"properties":{"mapbox": 70}},{"type":"Feature","geometry":{"type":"Point","coordinates":[37.901069736108184,-22.19181087333709]},"properties":{"mapbox": 49}},{"type":"Feature","geometry":{"type":"Point","coordinates":[97.93589439243078,-4.197207251563668]},"properties":{"mapbox": 91}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-38.304785611107945,-69.23459123354405]},"properties":{"mapbox": 81}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-173.49190623499453,-61.27010130789131]},"properties":{"mapbox": 38}},{"type":"Feature","geometry":{"type":"Point","coordinates":[0.9125038515776396,59.265824225731194]},"properties":{"mapbox": 12}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-143.5661626327783,-19.61784667801112]},"properties":{"mapbox": 27}},{"type":"Feature","geometry":{"type":"Point","coordinates":[140.19756828434765,45.24801858700812]},"properties":{"mapbox": 74}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-146.82779006659985,44.627577625215054]},"properties":{"mapbox": 32}},{"type":"Feature","geometry":{"type":"Point","coordinates":[128.03192960098386,-43.90957793220878]},"properties":{"mapbox": 23}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-114.60585057735443,-55.70240263827145]},"properties":{"mapbox": 74}},{"type":"Feature","geometry":{"type":"Point","coordinates":[80.67271177656949,-25.16001457348466]},"properties":{"mapbox": 96}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-37.23169421777129,41.82630505878478]},"properties":{"mapbox": 63}},{"type":"Feature","geometry":{"type":"Point","coordinates":[14.471303941681981,-67.27960975840688]},"properties":{"mapbox": 32}},{"type":"Feature","geometry":{"type":"Point","coordinates":[29.351948769763112,-37.61261290870607]},"properties":{"mapbox": 72}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-118.98626661859453,85.98206254653633]},"properties":{"mapbox": 75}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-133.93749067559838,-52.089063758030534]},"properties":{"mapbox": 72}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-49.4992710929364,53.66898624692112]},"properties":{"mapbox": 71}},{"type":"Feature","geometry":{"type":"Point","coordinates":[114.8944896273315,-31.78370848763734]},"properties":{"mapbox": 38}},{"type":"Feature","geometry":{"type":"Point","coordinates":[143.98385466076434,61.59199266694486]},"properties":{"mapbox": 8}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-145.0802127085626,-10.938919996842742]},"properties":{"mapbox": 3}},{"type":"Feature","geometry":{"type":"Point","coordinates":[169.7884490713477,52.00517109129578]},"properties":{"mapbox": 76}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-172.17302148230374,-42.275232123211026]},"properties":{"mapbox": 16}},{"type":"Feature","geometry":{"type":"Point","coordinates":[125.76004978269339,33.90714329201728]},"properties":{"mapbox": 50}},{"type":"Feature","geometry":{"type":"Point","coordinates":[114.21696455217898,89.54513410571963]},"properties":{"mapbox": 56}},{"type":"Feature","geometry":{"type":"Point","coordinates":[159.5182471163571,7.914179898798466]},"properties":{"mapbox": 73}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-160.38636619225144,9.510616078041494]},"properties":{"mapbox": 59}},{"type":"Feature","geometry":{"type":"Point","coordinates":[109.20965918339789,81.20597960427403]},"properties":{"mapbox": 69}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-25.193190816789865,54.03361869044602]},"properties":{"mapbox": 45}},{"type":"Feature","geometry":{"type":"Point","coordinates":[151.75552938133478,1.9545546267181635]},"properties":{"mapbox": 14}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-179.74409027025104,84.30114571005106]},"properties":{"mapbox": 47}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-21.349562080577016,86.70269302558154]},"properties":{"mapbox": 63}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-88.04809698835015,1.114083006978035]},"properties":{"mapbox": 9}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-1.8410969339311123,82.13660018518567]},"properties":{"mapbox": 60}},{"type":"Feature","geometry":{"type":"Point","coordinates":[131.50190021842718,5.552907628007233]},"properties":{"mapbox": 57}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-92.0619879104197,10.845119752921164]},"properties":{"mapbox": 3}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-48.7168724834919,48.143608435057104]},"properties":{"mapbox": 7}},{"type":"Feature","geometry":{"type":"Point","coordinates":[117.41884041577578,13.289493229240179]},"properties":{"mapbox": 54}},{"type":"Feature","geometry":{"type":"Point","coordinates":[64.85487101599574,-8.208351884968579]},"properties":{"mapbox": 80}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-77.34714634716511,1.4469616999849677]},"properties":{"mapbox": 97}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-93.29159198328853,-72.4770661862567]},"properties":{"mapbox": 74}},{"type":"Feature","geometry":{"type":"Point","coordinates":[66.42859940417111,-78.27111136168242]},"properties":{"mapbox": 41}},{"type":"Feature","geometry":{"type":"Point","coordinates":[96.73809905536473,19.116298486478627]},"properties":{"mapbox": 71}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-61.793883545324206,72.12481695692986]},"properties":{"mapbox": 95}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-98.2477710954845,37.38704076502472]},"properties":{"mapbox": 32}},{"type":"Feature","geometry":{"type":"Point","coordinates":[148.8963887002319,-50.787345939315856]},"properties":{"mapbox": 19}},{"type":"Feature","geometry":{"type":"Point","coordinates":[87.8617275506258,-77.5012998143211]},"properties":{"mapbox": 100}},{"type":"Feature","geometry":{"type":"Point","coordinates":[128.15870479680598,-3.630034038797021]},"properties":{"mapbox": 33}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-131.9270085543394,-68.15944958478212]},"properties":{"mapbox": 46}},{"type":"Feature","geometry":{"type":"Point","coordinates":[167.64817837625742,82.61665357276797]},"properties":{"mapbox": 68}},{"type":"Feature","geometry":{"type":"Point","coordinates":[4.287826269865036,-45.000482546165586]},"properties":{"mapbox": 44}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-138.9921465050429,-1.8423309596255422]},"properties":{"mapbox": 6}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-21.255824491381645,15.885210391134024]},"properties":{"mapbox": 21}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-157.9392747581005,75.71163214277476]},"properties":{"mapbox": 17}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-73.2338843587786,-4.1237724758684635]},"properties":{"mapbox": 26}},{"type":"Feature","geometry":{"type":"Point","coordinates":[104.13818456232548,-36.57757187727839]},"properties":{"mapbox": 71}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-56.15695391781628,69.78384184185416]},"properties":{"mapbox": 33}},{"type":"Feature","geometry":{"type":"Point","coordinates":[62.47151901014149,5.825139814987779]},"properties":{"mapbox": 5}},{"type":"Feature","geometry":{"type":"Point","coordinates":[105.97231487743556,-10.9131254022941]},"properties":{"mapbox": 10}},{"type":"Feature","geometry":{"type":"Point","coordinates":[63.177881855517626,-22.38561765756458]},"properties":{"mapbox": 36}},{"type":"Feature","geometry":{"type":"Point","coordinates":[178.24590135365725,68.59754270408303]},"properties":{"mapbox": 64}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-34.85676401294768,-30.68933938164264]},"properties":{"mapbox": 83}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-167.50994184985757,64.53059337101877]},"properties":{"mapbox": 95}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-108.98514504544437,17.155385515652597]},"properties":{"mapbox": 19}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-148.04009104147553,-24.630167903378606]},"properties":{"mapbox": 76}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-168.31853373907506,68.53996183723211]},"properties":{"mapbox": 37}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-79.39349262043834,22.610792331397533]},"properties":{"mapbox": 45}},{"type":"Feature","geometry":{"type":"Point","coordinates":[128.11431717127562,-17.222693203948438]},"properties":{"mapbox": 9}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-95.66886300221086,20.973391821607947]},"properties":{"mapbox": 6}},{"type":"Feature","geometry":{"type":"Point","coordinates":[59.1321013122797,77.73103675805032]},"properties":{"mapbox": 44}},{"type":"Feature","geometry":{"type":"Point","coordinates":[64.46210349909961,-3.797665061429143]},"properties":{"mapbox": 33}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-152.65658154152334,61.17343520745635]},"properties":{"mapbox": 96}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-5.032834801822901,-80.51403128542006]},"properties":{"mapbox": 73}},{"type":"Feature","geometry":{"type":"Point","coordinates":[149.14262588135898,14.811443709768355]},"properties":{"mapbox": 71}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-117.67312103882432,35.601952923461795]},"properties":{"mapbox": 14}},{"type":"Feature","geometry":{"type":"Point","coordinates":[58.696921449154615,76.93483407609165]},"properties":{"mapbox": 95}},{"type":"Feature","geometry":{"type":"Point","coordinates":[115.12597859837115,-27.19985560979694]},"properties":{"mapbox": 76}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-87.42650183849037,60.63311350531876]},"properties":{"mapbox": 12}},{"type":"Feature","geometry":{"type":"Point","coordinates":[38.965260637924075,-12.436161800287664]},"properties":{"mapbox": 13}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-124.12975067272782,46.57280751038343]},"properties":{"mapbox": 44}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-79.01402894407511,59.47802145034075]},"properties":{"mapbox": 36}},{"type":"Feature","geometry":{"type":"Point","coordinates":[3.1428339891135693,-50.61879039276391]},"properties":{"mapbox": 84}},{"type":"Feature","geometry":{"type":"Point","coordinates":[150.56984103284776,-67.89576589129865]},"properties":{"mapbox": 27}},{"type":"Feature","geometry":{"type":"Point","coordinates":[162.56736790761352,-81.48028237279505]},"properties":{"mapbox": 10}},{"type":"Feature","geometry":{"type":"Point","coordinates":[143.31506738439202,44.763501500710845]},"properties":{"mapbox": 59}},{"type":"Feature","geometry":{"type":"Point","coordinates":[87.38029123283923,82.75920418091118]},"properties":{"mapbox": 47}},{"type":"Feature","geometry":{"type":"Point","coordinates":[161.4928848668933,23.620088850148022]},"properties":{"mapbox": 59}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-46.51166198775172,17.211917680688202]},"properties":{"mapbox": 55}},{"type":"Feature","geometry":{"type":"Point","coordinates":[76.91065756604075,-3.8099643727764487]},"properties":{"mapbox": 82}},{"type":"Feature","geometry":{"type":"Point","coordinates":[68.9309889357537,-33.26238655485213]},"properties":{"mapbox": 13}},{"type":"Feature","geometry":{"type":"Point","coordinates":[128.56363772414625,-57.36148780211806]},"properties":{"mapbox": 6}},{"type":"Feature","geometry":{"type":"Point","coordinates":[1.0216147731989622,-66.06437901500612]},"properties":{"mapbox": 13}},{"type":"Feature","geometry":{"type":"Point","coordinates":[112.04996204003692,87.48259470798075]},"properties":{"mapbox": 36}},{"type":"Feature","geometry":{"type":"Point","coordinates":[16.097388714551926,41.1862831749022]},"properties":{"mapbox": 72}},{"type":"Feature","geometry":{"type":"Point","coordinates":[41.597789945080876,82.33694112394005]},"properties":{"mapbox": 48}},{"type":"Feature","geometry":{"type":"Point","coordinates":[170.73053332976997,20.212005176581442]},"properties":{"mapbox": 89}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-51.28361273556948,12.821408319287002]},"properties":{"mapbox": 10}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-126.20331206358969,32.627388993278146]},"properties":{"mapbox": 73}},{"type":"Feature","geometry":{"type":"Point","coordinates":[68.47599014639854,-48.73019208200276]},"properties":{"mapbox": 8}},{"type":"Feature","geometry":{"type":"Point","coordinates":[7.503653643652797,-61.4725673943758]},"properties":{"mapbox": 6}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-176.48825615644455,-51.726085441187024]},"properties":{"mapbox": 50}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-31.279294649139047,28.34117544349283]},"properties":{"mapbox": 2}},{"type":"Feature","geometry":{"type":"Point","coordinates":[7.088211653754115,-24.105680999346077]},"properties":{"mapbox": 33}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-33.13733744435012,-7.600142173469067]},"properties":{"mapbox": 35}},{"type":"Feature","geometry":{"type":"Point","coordinates":[118.55583686381578,-53.2780737709254]},"properties":{"mapbox": 42}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-141.7861074116081,10.011035855859518]},"properties":{"mapbox": 87}},{"type":"Feature","geometry":{"type":"Point","coordinates":[22.935774559155107,44.0105700166896]},"properties":{"mapbox": 92}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-89.36553846113384,4.018968511372805]},"properties":{"mapbox": 26}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-3.784685432910919,6.479591270908713]},"properties":{"mapbox": 5}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-42.49874101951718,-19.924558368511498]},"properties":{"mapbox": 8}},{"type":"Feature","geometry":{"type":"Point","coordinates":[86.19898351840675,74.238501470536]},"properties":{"mapbox": 35}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-167.01308970339596,-19.07945587299764]},"properties":{"mapbox": 28}},{"type":"Feature","geometry":{"type":"Point","coordinates":[7.472661640495062,55.85181953385472]},"properties":{"mapbox": 68}},{"type":"Feature","geometry":{"type":"Point","coordinates":[10.16387709416449,9.010712709277868]},"properties":{"mapbox": 29}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-111.77245139144361,-63.788525252602994]},"properties":{"mapbox": 77}},{"type":"Feature","geometry":{"type":"Point","coordinates":[132.7932282909751,59.112535561434925]},"properties":{"mapbox": 98}},{"type":"Feature","geometry":{"type":"Point","coordinates":[32.11806700564921,42.33946813736111]},"properties":{"mapbox": 93}},{"type":"Feature","geometry":{"type":"Point","coordinates":[84.23201076686382,74.00251803919673]},"properties":{"mapbox": 40}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-159.94870654307306,83.00069867167622]},"properties":{"mapbox": 12}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-74.33175626210868,40.2495814813301]},"properties":{"mapbox": 12}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-12.03122053295374,53.76494717784226]},"properties":{"mapbox": 69}},{"type":"Feature","geometry":{"type":"Point","coordinates":[38.51891281083226,60.39851380046457]},"properties":{"mapbox": 8}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-177.92527955956757,-27.078501577489078]},"properties":{"mapbox": 43}},{"type":"Feature","geometry":{"type":"Point","coordinates":[27.8085361327976,-89.31377589236945]},"properties":{"mapbox": 99}},{"type":"Feature","geometry":{"type":"Point","coordinates":[31.712982952594757,-62.790777194313705]},"properties":{"mapbox": 20}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-110.4998944234103,39.87759333103895]},"properties":{"mapbox": 78}},{"type":"Feature","geometry":{"type":"Point","coordinates":[6.665234751999378,74.6124066459015]},"properties":{"mapbox": 69}},{"type":"Feature","geometry":{"type":"Point","coordinates":[142.03034416772425,44.22287402674556]},"properties":{"mapbox": 83}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-146.78715887479484,85.84780478384346]},"properties":{"mapbox": 19}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-20.139846755191684,-31.440163403749466]},"properties":{"mapbox": 63}},{"type":"Feature","geometry":{"type":"Point","coordinates":[53.40938273817301,-33.084354805760086]},"properties":{"mapbox": 74}},{"type":"Feature","geometry":{"type":"Point","coordinates":[168.71282072737813,-71.07424995861948]},"properties":{"mapbox": 52}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-48.08681420981884,36.95648339577019]},"properties":{"mapbox": 25}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-116.24423461966217,-49.2674556048587]},"properties":{"mapbox": 64}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-177.43650136515498,89.10378319211304]},"properties":{"mapbox": 66}},{"type":"Feature","geometry":{"type":"Point","coordinates":[117.56597299128771,-60.207746461965144]},"properties":{"mapbox": 58}},{"type":"Feature","geometry":{"type":"Point","coordinates":[47.808708446100354,-62.97403304837644]},"properties":{"mapbox": 100}},{"type":"Feature","geometry":{"type":"Point","coordinates":[8.761591063812375,87.67553203739226]},"properties":{"mapbox": 38}},{"type":"Feature","geometry":{"type":"Point","coordinates":[173.6599436122924,-47.94121703132987]},"properties":{"mapbox": 90}},{"type":"Feature","geometry":{"type":"Point","coordinates":[145.28842580504715,71.71248435974121]},"properties":{"mapbox": 12}},{"type":"Feature","geometry":{"type":"Point","coordinates":[22.99990575760603,-56.52551684528589]},"properties":{"mapbox": 88}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-113.92999400384724,-6.061111846938729]},"properties":{"mapbox": 62}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-112.70329082384706,40.00028905458748]},"properties":{"mapbox": 81}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-157.0024934783578,-25.5547647876665]},"properties":{"mapbox": 74}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-157.7265284769237,-8.688656771555543]},"properties":{"mapbox": 47}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-65.3096076194197,-16.137895099818707]},"properties":{"mapbox": 20}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-100.8437733259052,33.71473289094865]},"properties":{"mapbox": 6}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-135.32863640226424,15.69878248963505]},"properties":{"mapbox": 95}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-175.34784416668117,-81.23251222539693]},"properties":{"mapbox": 34}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-176.63457275368273,27.68016146030277]},"properties":{"mapbox": 72}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-3.8195206224918365,21.758858459070325]},"properties":{"mapbox": 5}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-7.554967403411865,-87.93070897925645]},"properties":{"mapbox": 90}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-148.34330152720213,15.5256537348032]},"properties":{"mapbox": 62}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-22.70371813327074,-88.98335041478276]},"properties":{"mapbox": 95}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-76.80486177094281,87.56499422248453]},"properties":{"mapbox": 56}},{"type":"Feature","geometry":{"type":"Point","coordinates":[94.73564176820219,-23.18279926199466]},"properties":{"mapbox": 17}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-122.16504757292569,10.985643323510885]},"properties":{"mapbox": 32}},{"type":"Feature","geometry":{"type":"Point","coordinates":[22.098287483677268,-74.52641573734581]},"properties":{"mapbox": 70}},{"type":"Feature","geometry":{"type":"Point","coordinates":[97.66668090596795,41.98179124854505]},"properties":{"mapbox": 95}},{"type":"Feature","geometry":{"type":"Point","coordinates":[13.54695713147521,-38.86961947660893]},"properties":{"mapbox": 43}},{"type":"Feature","geometry":{"type":"Point","coordinates":[84.5068723987788,-89.03763505630195]},"properties":{"mapbox": 33}},{"type":"Feature","geometry":{"type":"Point","coordinates":[105.55773354135454,-21.485011079348624]},"properties":{"mapbox": 67}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-6.420687530189753,28.988473741337657]},"properties":{"mapbox": 34}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-57.10427041165531,-84.32162466924638]},"properties":{"mapbox": 71}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-27.326627410948277,-25.176213565282524]},"properties":{"mapbox": 12}},{"type":"Feature","geometry":{"type":"Point","coordinates":[50.39919488132,5.478498330339789]},"properties":{"mapbox": 61}},{"type":"Feature","geometry":{"type":"Point","coordinates":[55.15531028620899,-82.91263449937105]},"properties":{"mapbox": 36}},{"type":"Feature","geometry":{"type":"Point","coordinates":[17.161540472880006,10.206110067665577]},"properties":{"mapbox": 68}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-6.007457105442882,21.947381501086056]},"properties":{"mapbox": 8}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-22.560217771679163,7.8930790442973375]},"properties":{"mapbox": 97}},{"type":"Feature","geometry":{"type":"Point","coordinates":[65.83445104770362,-76.19783260859549]},"properties":{"mapbox": 84}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-79.10166140645742,-57.76479174848646]},"properties":{"mapbox": 30}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-52.24335338920355,28.128394335508347]},"properties":{"mapbox": 10}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-167.75034346617758,32.644973010756075]},"properties":{"mapbox": 73}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-60.84821074269712,68.28051251359284]},"properties":{"mapbox": 36}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-124.8287599068135,-4.834600402973592]},"properties":{"mapbox": 61}},{"type":"Feature","geometry":{"type":"Point","coordinates":[12.214657980948687,-7.677580271847546]},"properties":{"mapbox": 9}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-159.34292058460414,0.939008267596364]},"properties":{"mapbox": 70}},{"type":"Feature","geometry":{"type":"Point","coordinates":[34.17610913515091,-53.271518871188164]},"properties":{"mapbox": 87}},{"type":"Feature","geometry":{"type":"Point","coordinates":[35.44008119031787,-45.07839510217309]},"properties":{"mapbox": 33}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-60.85252775810659,46.61097060889006]},"properties":{"mapbox": 24}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-96.09791326336563,33.897040290758014]},"properties":{"mapbox": 23}},{"type":"Feature","geometry":{"type":"Point","coordinates":[10.84976764395833,-23.576332926750183]},"properties":{"mapbox": 73}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-99.40655621699989,40.975414481945336]},"properties":{"mapbox": 25}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-159.6056765690446,-54.53383868560195]},"properties":{"mapbox": 96}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-111.82251306250691,6.798568242229521]},"properties":{"mapbox": 64}},{"type":"Feature","geometry":{"type":"Point","coordinates":[51.06023283675313,-32.82271181233227]},"properties":{"mapbox": 92}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-16.16575051099062,-88.20602964609861]},"properties":{"mapbox": 94}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-108.40289345942438,74.52982507646084]},"properties":{"mapbox": 59}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-21.357374098151922,33.79067603498697]},"properties":{"mapbox": 86}},{"type":"Feature","geometry":{"type":"Point","coordinates":[77.00604454614222,-45.98769270814955]},"properties":{"mapbox": 39}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-153.91111800447106,-53.52022921666503]},"properties":{"mapbox": 66}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-151.2720881961286,-85.24132933001965]},"properties":{"mapbox": 1}},{"type":"Feature","geometry":{"type":"Point","coordinates":[160.26194446720183,74.5986429322511]},"properties":{"mapbox": 70}},{"type":"Feature","geometry":{"type":"Point","coordinates":[42.970063062384725,38.725563855841756]},"properties":{"mapbox": 92}},{"type":"Feature","geometry":{"type":"Point","coordinates":[111.6670954786241,23.52170533966273]},"properties":{"mapbox": 35}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-73.96401124075055,-33.71974874753505]},"properties":{"mapbox": 47}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-13.79294553771615,-7.111376929096878]},"properties":{"mapbox": 88}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-9.01755946688354,53.93363012932241]},"properties":{"mapbox": 31}},{"type":"Feature","geometry":{"type":"Point","coordinates":[42.33486316166818,-28.878679485060275]},"properties":{"mapbox": 30}},{"type":"Feature","geometry":{"type":"Point","coordinates":[122.18439082615077,-54.9345934856683]},"properties":{"mapbox": 32}},{"type":"Feature","geometry":{"type":"Point","coordinates":[71.76295515149832,-70.35853689070791]},"properties":{"mapbox": 88}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-156.8277490697801,-70.29983284883201]},"properties":{"mapbox": 34}},{"type":"Feature","geometry":{"type":"Point","coordinates":[87.3418350610882,59.3576778145507]},"properties":{"mapbox": 59}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-47.04308554530144,69.09951736684889]},"properties":{"mapbox": 34}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-88.75740104354918,1.9157659448683262]},"properties":{"mapbox": 14}},{"type":"Feature","geometry":{"type":"Point","coordinates":[147.92443921789527,55.991847859695554]},"properties":{"mapbox": 8}},{"type":"Feature","geometry":{"type":"Point","coordinates":[128.52962773293257,26.396703282371163]},"properties":{"mapbox": 12}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-94.97377046383917,56.86219673138112]},"properties":{"mapbox": 85}},{"type":"Feature","geometry":{"type":"Point","coordinates":[99.80871565639973,32.0507845049724]},"properties":{"mapbox": 21}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-67.59248885326087,47.07945814356208]},"properties":{"mapbox": 85}},{"type":"Feature","geometry":{"type":"Point","coordinates":[146.41556630842388,-3.0061532696709037]},"properties":{"mapbox": 21}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-21.348323486745358,62.35960899386555]},"properties":{"mapbox": 79}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-165.7372110709548,-78.70617899112403]},"properties":{"mapbox": 77}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-87.8734134323895,-58.981731394305825]},"properties":{"mapbox": 38}},{"type":"Feature","geometry":{"type":"Point","coordinates":[118.48579172976315,-0.04956013988703489]},"properties":{"mapbox": 9}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-23.929348513484,54.68587295617908]},"properties":{"mapbox": 46}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-178.6426289845258,-73.14786633476615]},"properties":{"mapbox": 83}},{"type":"Feature","geometry":{"type":"Point","coordinates":[30.740530053153634,58.38801684323698]},"properties":{"mapbox": 35}},{"type":"Feature","geometry":{"type":"Point","coordinates":[144.24143322743475,-74.4166634324938]},"properties":{"mapbox": 12}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-173.21241904981434,-23.111277068965137]},"properties":{"mapbox": 20}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-107.08533606491983,77.8691914351657]},"properties":{"mapbox": 90}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-3.0583822913467884,-8.07470298372209]},"properties":{"mapbox": 77}},{"type":"Feature","geometry":{"type":"Point","coordinates":[142.1726858150214,66.06418832670897]},"properties":{"mapbox": 100}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-72.12944003753364,1.034755501896143]},"properties":{"mapbox": 88}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-28.326070625334978,44.35426794458181]},"properties":{"mapbox": 99}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-156.58604733645916,9.969834652729332]},"properties":{"mapbox": 68}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-46.83521627448499,-67.70592160988599]},"properties":{"mapbox": 48}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-66.11459956504405,19.916619365103543]},"properties":{"mapbox": 73}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-57.6163783762604,-74.36286498326808]},"properties":{"mapbox": 16}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-21.96260266005993,1.3756098272278905]},"properties":{"mapbox": 78}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-167.64215279370546,-51.04974158573896]},"properties":{"mapbox": 14}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-176.17273692972958,-68.08571871835738]},"properties":{"mapbox": 33}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-14.52274871058762,-31.73920582048595]},"properties":{"mapbox": 29}},{"type":"Feature","geometry":{"type":"Point","coordinates":[14.299073182046413,-23.6265701148659]},"properties":{"mapbox": 70}},{"type":"Feature","geometry":{"type":"Point","coordinates":[154.40124157816172,0.8706196490675211]},"properties":{"mapbox": 39}},{"type":"Feature","geometry":{"type":"Point","coordinates":[132.24152429960668,48.12327150721103]},"properties":{"mapbox": 66}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-174.7616035491228,77.95839596539736]},"properties":{"mapbox": 10}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-49.875975362956524,-82.87523021455854]},"properties":{"mapbox": 81}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-46.84728378430009,-13.968596700578928]},"properties":{"mapbox": 11}},{"type":"Feature","geometry":{"type":"Point","coordinates":[1.8018254544585943,-77.04069994390011]},"properties":{"mapbox": 41}},{"type":"Feature","geometry":{"type":"Point","coordinates":[47.43022277019918,69.33753441553563]},"properties":{"mapbox": 34}},{"type":"Feature","geometry":{"type":"Point","coordinates":[30.34634574316442,84.55966786947101]},"properties":{"mapbox": 8}},{"type":"Feature","geometry":{"type":"Point","coordinates":[112.62707585468888,-0.7477007573470473]},"properties":{"mapbox": 42}},{"type":"Feature","geometry":{"type":"Point","coordinates":[38.783310893923044,-65.76383171137422]},"properties":{"mapbox": 32}},{"type":"Feature","geometry":{"type":"Point","coordinates":[4.004682647064328,54.02404320426285]},"properties":{"mapbox": 41}},{"type":"Feature","geometry":{"type":"Point","coordinates":[118.39305385015905,78.25564247556031]},"properties":{"mapbox": 43}},{"type":"Feature","geometry":{"type":"Point","coordinates":[172.90896465070546,-36.173745822161436]},"properties":{"mapbox": 98}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-41.62649025209248,28.12089948914945]},"properties":{"mapbox": 15}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-105.92365097254515,-67.9277648543939]},"properties":{"mapbox": 30}},{"type":"Feature","geometry":{"type":"Point","coordinates":[66.90812934190035,19.56935254856944]},"properties":{"mapbox": 69}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-168.35342357866466,-40.26683282107115]},"properties":{"mapbox": 60}},{"type":"Feature","geometry":{"type":"Point","coordinates":[62.108105421066284,82.71303778979927]},"properties":{"mapbox": 83}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-111.95974258705974,50.35987776238471]},"properties":{"mapbox": 97}},{"type":"Feature","geometry":{"type":"Point","coordinates":[103.6906898394227,53.36193860974163]},"properties":{"mapbox": 66}},{"type":"Feature","geometry":{"type":"Point","coordinates":[75.43049778789282,-44.569205422885716]},"properties":{"mapbox": 70}},{"type":"Feature","geometry":{"type":"Point","coordinates":[159.96820813044906,-54.039471773430705]},"properties":{"mapbox": 71}},{"type":"Feature","geometry":{"type":"Point","coordinates":[45.07046598941088,-85.21366498433053]},"properties":{"mapbox": 19}},{"type":"Feature","geometry":{"type":"Point","coordinates":[138.77374218776822,-61.99772702995688]},"properties":{"mapbox": 29}},{"type":"Feature","geometry":{"type":"Point","coordinates":[128.1855802796781,71.63413338828832]},"properties":{"mapbox": 86}},{"type":"Feature","geometry":{"type":"Point","coordinates":[87.69008344039321,20.79491708893329]},"properties":{"mapbox": 37}},{"type":"Feature","geometry":{"type":"Point","coordinates":[65.77875749208033,-34.482794841751456]},"properties":{"mapbox": 55}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-159.52017948031425,7.437892626039684]},"properties":{"mapbox": 86}},{"type":"Feature","geometry":{"type":"Point","coordinates":[25.50886963494122,-48.584610619582236]},"properties":{"mapbox": 69}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-84.51872759498656,-35.723060527816415]},"properties":{"mapbox": 75}},{"type":"Feature","geometry":{"type":"Point","coordinates":[148.35694441571832,50.37374516017735]},"properties":{"mapbox": 96}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-41.84343854896724,24.71985979937017]},"properties":{"mapbox": 99}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-172.19425158575177,-70.63582307193428]},"properties":{"mapbox": 48}},{"type":"Feature","geometry":{"type":"Point","coordinates":[77.11480719968677,28.662969111464918]},"properties":{"mapbox": 71}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-98.62663102336228,-17.722317832522094]},"properties":{"mapbox": 67}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-67.42590799927711,-81.14906660746783]},"properties":{"mapbox": 13}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-15.245613427832723,-0.23437766823917627]},"properties":{"mapbox": 54}},{"type":"Feature","geometry":{"type":"Point","coordinates":[53.54975648224354,20.513179572299123]},"properties":{"mapbox": 8}},{"type":"Feature","geometry":{"type":"Point","coordinates":[157.46731761842966,-11.566166351549327]},"properties":{"mapbox": 32}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-52.09023529663682,6.282507749274373]},"properties":{"mapbox": 51}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-83.07940570637584,-11.092898910865188]},"properties":{"mapbox": 75}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-34.011048171669245,-63.91121062450111]},"properties":{"mapbox": 13}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-42.468092339113355,31.389638339169323]},"properties":{"mapbox": 62}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-178.01955727860332,11.634456650353968]},"properties":{"mapbox": 69}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-64.60163025185466,-16.182731157168746]},"properties":{"mapbox": 76}},{"type":"Feature","geometry":{"type":"Point","coordinates":[93.32587975077331,9.498496516607702]},"properties":{"mapbox": 99}},{"type":"Feature","geometry":{"type":"Point","coordinates":[138.447703756392,63.300821115262806]},"properties":{"mapbox": 84}},{"type":"Feature","geometry":{"type":"Point","coordinates":[107.98797860741615,64.21587327495217]},"properties":{"mapbox": 60}},{"type":"Feature","geometry":{"type":"Point","coordinates":[44.89903402514756,11.920283697545528]},"properties":{"mapbox": 49}},{"type":"Feature","geometry":{"type":"Point","coordinates":[97.09358906373382,66.50570872239769]},"properties":{"mapbox": 34}},{"type":"Feature","geometry":{"type":"Point","coordinates":[135.55744014680386,-39.8464760184288]},"properties":{"mapbox": 44}},{"type":"Feature","geometry":{"type":"Point","coordinates":[24.461368694901466,-57.544735819101334]},"properties":{"mapbox": 26}},{"type":"Feature","geometry":{"type":"Point","coordinates":[86.75982713699341,-37.044484368525445]},"properties":{"mapbox": 58}},{"type":"Feature","geometry":{"type":"Point","coordinates":[154.97563261538744,46.89816097263247]},"properties":{"mapbox": 94}},{"type":"Feature","geometry":{"type":"Point","coordinates":[63.62180722877383,48.797338963486254]},"properties":{"mapbox": 44}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-81.6332977451384,-69.5285688014701]},"properties":{"mapbox": 31}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-125.86669416166842,11.151868575252593]},"properties":{"mapbox": 52}},{"type":"Feature","geometry":{"type":"Point","coordinates":[24.033637242391706,1.986960656940937]},"properties":{"mapbox": 83}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-90.83612258546054,41.83964733034372]},"properties":{"mapbox": 89}},{"type":"Feature","geometry":{"type":"Point","coordinates":[141.40925436280668,-56.18986068293452]},"properties":{"mapbox": 65}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-74.97196728363633,69.41084241494536]},"properties":{"mapbox": 45}},{"type":"Feature","geometry":{"type":"Point","coordinates":[86.00875438190997,-37.44377627503127]},"properties":{"mapbox": 66}},{"type":"Feature","geometry":{"type":"Point","coordinates":[0.16310655511915684,30.11453411076218]},"properties":{"mapbox": 5}},{"type":"Feature","geometry":{"type":"Point","coordinates":[59.132625265046954,80.28071338310838]},"properties":{"mapbox": 44}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-26.065974170342088,-6.162758809514344]},"properties":{"mapbox": 3}},{"type":"Feature","geometry":{"type":"Point","coordinates":[157.86116070114076,-55.637453868985176]},"properties":{"mapbox": 21}},{"type":"Feature","geometry":{"type":"Point","coordinates":[45.68473267368972,-44.569785576313734]},"properties":{"mapbox": 60}},{"type":"Feature","geometry":{"type":"Point","coordinates":[151.67410547845066,-58.36482801474631]},"properties":{"mapbox": 14}},{"type":"Feature","geometry":{"type":"Point","coordinates":[90.00907181762159,-75.99146893713623]},"properties":{"mapbox": 4}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-104.12095547653735,-15.194700784049928]},"properties":{"mapbox": 52}},{"type":"Feature","geometry":{"type":"Point","coordinates":[117.50006223097444,28.45345518551767]},"properties":{"mapbox": 63}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-155.98592184484005,76.06090474873781]},"properties":{"mapbox": 92}},{"type":"Feature","geometry":{"type":"Point","coordinates":[75.13844445347786,1.7793576791882515]},"properties":{"mapbox": 25}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-145.20926292054355,78.05272841826081]},"properties":{"mapbox": 93}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-128.45609254203737,-6.3851268868893385]},"properties":{"mapbox": 55}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-81.19094272144139,-29.85254536382854]},"properties":{"mapbox": 70}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-165.57452034205198,-38.7693885108456]},"properties":{"mapbox": 6}},{"type":"Feature","geometry":{"type":"Point","coordinates":[48.1255017220974,-76.26653064507991]},"properties":{"mapbox": 75}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-73.45806020312011,55.7819620706141]},"properties":{"mapbox": 98}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-102.19109049998224,-2.3640777356922626]},"properties":{"mapbox": 34}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-2.548731230199337,-42.93387838639319]},"properties":{"mapbox": 71}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-157.32786169275641,50.21063269581646]},"properties":{"mapbox": 47}},{"type":"Feature","geometry":{"type":"Point","coordinates":[70.70466636680067,-7.382422029040754]},"properties":{"mapbox": 96}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-102.35983388498425,44.1893474617973]},"properties":{"mapbox": 3}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-79.82309818267822,61.13497647922486]},"properties":{"mapbox": 64}},{"type":"Feature","geometry":{"type":"Point","coordinates":[49.999863831326365,15.985758653841913]},"properties":{"mapbox": 92}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-24.133144933730364,2.1917387237772346]},"properties":{"mapbox": 58}},{"type":"Feature","geometry":{"type":"Point","coordinates":[123.59778338111937,-57.69319223240018]},"properties":{"mapbox": 60}},{"type":"Feature","geometry":{"type":"Point","coordinates":[94.0684381686151,-66.65535859297961]},"properties":{"mapbox": 47}},{"type":"Feature","geometry":{"type":"Point","coordinates":[157.03033162280917,89.06510858796537]},"properties":{"mapbox": 100}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-114.44523193873465,-3.16591611597687]},"properties":{"mapbox": 69}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-155.92443882487714,79.3146219290793]},"properties":{"mapbox": 23}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-138.58794959262013,-83.43364333268255]},"properties":{"mapbox": 77}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-28.40943267568946,-12.023972407914698]},"properties":{"mapbox": 85}},{"type":"Feature","geometry":{"type":"Point","coordinates":[153.57163391076028,-14.328520400449634]},"properties":{"mapbox": 15}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-131.6473619453609,-31.539645195007324]},"properties":{"mapbox": 22}},{"type":"Feature","geometry":{"type":"Point","coordinates":[8.759269108995795,-83.55959844309837]},"properties":{"mapbox": 49}},{"type":"Feature","geometry":{"type":"Point","coordinates":[75.3498440142721,-65.3848987352103]},"properties":{"mapbox": 18}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-173.8281673192978,-61.68116744142026]},"properties":{"mapbox": 36}},{"type":"Feature","geometry":{"type":"Point","coordinates":[64.335723426193,63.000016110017896]},"properties":{"mapbox": 8}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-154.10092351958156,20.27713805437088]},"properties":{"mapbox": 92}},{"type":"Feature","geometry":{"type":"Point","coordinates":[107.18762369826436,-75.27225931640714]},"properties":{"mapbox": 89}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-80.6641783285886,89.01925991289318]},"properties":{"mapbox": 69}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-87.12357725948095,-26.60714873112738]},"properties":{"mapbox": 13}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-62.47553293593228,-43.975366819649935]},"properties":{"mapbox": 70}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-56.76539660431445,-52.99163174349815]},"properties":{"mapbox": 3}},{"type":"Feature","geometry":{"type":"Point","coordinates":[168.49611894227564,75.842468412593]},"properties":{"mapbox": 51}},{"type":"Feature","geometry":{"type":"Point","coordinates":[155.85793999023736,58.690635985694826]},"properties":{"mapbox": 67}},{"type":"Feature","geometry":{"type":"Point","coordinates":[60.55452469736338,-52.98390756826848]},"properties":{"mapbox": 52}},{"type":"Feature","geometry":{"type":"Point","coordinates":[142.33582170680165,-60.806593000888824]},"properties":{"mapbox": 68}},{"type":"Feature","geometry":{"type":"Point","coordinates":[32.24343739449978,83.23412289842963]},"properties":{"mapbox": 82}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-128.7367181573063,-44.541783523745835]},"properties":{"mapbox": 40}},{"type":"Feature","geometry":{"type":"Point","coordinates":[26.811355277895927,-48.45495829358697]},"properties":{"mapbox": 57}},{"type":"Feature","geometry":{"type":"Point","coordinates":[53.918052446097136,-50.31594221480191]},"properties":{"mapbox": 9}},{"type":"Feature","geometry":{"type":"Point","coordinates":[46.152025954797864,82.9171155905351]},"properties":{"mapbox": 82}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-148.16632015630603,-80.6247699726373]},"properties":{"mapbox": 92}},{"type":"Feature","geometry":{"type":"Point","coordinates":[54.16946595534682,89.37623892445117]},"properties":{"mapbox": 15}},{"type":"Feature","geometry":{"type":"Point","coordinates":[39.428468411788344,71.39732718933374]},"properties":{"mapbox": 88}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-29.818561356514692,-87.24294427782297]},"properties":{"mapbox": 4}},{"type":"Feature","geometry":{"type":"Point","coordinates":[99.59268576465547,14.529108982533216]},"properties":{"mapbox": 40}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-30.27795326896012,51.272379979491234]},"properties":{"mapbox": 57}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-28.50434297695756,22.147771390154958]},"properties":{"mapbox": 65}},{"type":"Feature","geometry":{"type":"Point","coordinates":[98.66102700121701,-26.98136743158102]},"properties":{"mapbox": 65}},{"type":"Feature","geometry":{"type":"Point","coordinates":[18.10657256282866,18.187531353905797]},"properties":{"mapbox": 3}},{"type":"Feature","geometry":{"type":"Point","coordinates":[88.62086964771152,27.71508181001991]},"properties":{"mapbox": 98}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-165.33975077793002,-74.81050299480557]},"properties":{"mapbox": 63}},{"type":"Feature","geometry":{"type":"Point","coordinates":[146.61559757776558,-30.2143995417282]},"properties":{"mapbox": 58}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-31.76683575846255,-62.23036696203053]},"properties":{"mapbox": 68}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-171.3705331273377,53.36500286590308]},"properties":{"mapbox": 56}},{"type":"Feature","geometry":{"type":"Point","coordinates":[99.11290822550654,-42.837288728915155]},"properties":{"mapbox": 87}},{"type":"Feature","geometry":{"type":"Point","coordinates":[144.2002857942134,35.85235783830285]},"properties":{"mapbox": 80}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-94.01839108206332,-27.737779919989407]},"properties":{"mapbox": 77}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-19.553628768771887,87.03391993883997]},"properties":{"mapbox": 21}},{"type":"Feature","geometry":{"type":"Point","coordinates":[178.0183859076351,21.385139278136194]},"properties":{"mapbox": 61}},{"type":"Feature","geometry":{"type":"Point","coordinates":[71.92996750585735,16.57038039062172]},"properties":{"mapbox": 63}},{"type":"Feature","geometry":{"type":"Point","coordinates":[122.1935795713216,58.11467200051993]},"properties":{"mapbox": 53}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-147.9172930587083,10.932626193389297]},"properties":{"mapbox": 10}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-92.24035447463393,26.318431729450822]},"properties":{"mapbox": 75}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-87.07592094317079,47.604615851305425]},"properties":{"mapbox": 24}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-127.08934234455228,62.0113266678527]},"properties":{"mapbox": 15}},{"type":"Feature","geometry":{"type":"Point","coordinates":[16.953506162390113,-56.5413715923205]},"properties":{"mapbox": 30}},{"type":"Feature","geometry":{"type":"Point","coordinates":[2.1253615524619818,-57.9912473866716]},"properties":{"mapbox": 26}},{"type":"Feature","geometry":{"type":"Point","coordinates":[19.528056671842933,-1.8807247700169683]},"properties":{"mapbox": 34}},{"type":"Feature","geometry":{"type":"Point","coordinates":[57.66796647571027,41.272982941009104]},"properties":{"mapbox": 97}},{"type":"Feature","geometry":{"type":"Point","coordinates":[25.238397186622024,21.62111126817763]},"properties":{"mapbox": 41}},{"type":"Feature","geometry":{"type":"Point","coordinates":[21.714930161833763,65.44598327949643]},"properties":{"mapbox": 72}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-10.444665905088186,-14.791977368295193]},"properties":{"mapbox": 29}},{"type":"Feature","geometry":{"type":"Point","coordinates":[138.9792958740145,64.79380751028657]},"properties":{"mapbox": 8}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-107.37737297080457,-37.59859719313681]},"properties":{"mapbox": 54}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-15.222228253260255,32.659749845042825]},"properties":{"mapbox": 79}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-136.06521433219314,-49.82728966511786]},"properties":{"mapbox": 59}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-139.78533430024981,-80.49050428904593]},"properties":{"mapbox": 15}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-65.04433865658939,83.0693593248725]},"properties":{"mapbox": 30}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-58.45413375645876,4.437992465682328]},"properties":{"mapbox": 66}},{"type":"Feature","geometry":{"type":"Point","coordinates":[142.07782488316298,-63.647979008965194]},"properties":{"mapbox": 25}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-77.22275203093886,60.31969440635294]},"properties":{"mapbox": 39}},{"type":"Feature","geometry":{"type":"Point","coordinates":[118.87979784049094,46.770676667802036]},"properties":{"mapbox": 2}},{"type":"Feature","geometry":{"type":"Point","coordinates":[15.858520232141018,-18.45026441384107]},"properties":{"mapbox": 96}},{"type":"Feature","geometry":{"type":"Point","coordinates":[4.697578875347972,-89.0603848407045]},"properties":{"mapbox": 53}},{"type":"Feature","geometry":{"type":"Point","coordinates":[0.29192584566771984,-39.75866707507521]},"properties":{"mapbox": 28}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-41.20185096748173,-68.3810604410246]},"properties":{"mapbox": 71}},{"type":"Feature","geometry":{"type":"Point","coordinates":[82.81948900781572,-75.26050860062242]},"properties":{"mapbox": 15}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-45.95500378869474,68.84790445212275]},"properties":{"mapbox": 61}},{"type":"Feature","geometry":{"type":"Point","coordinates":[36.90243864431977,19.058164414018393]},"properties":{"mapbox": 84}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-126.76482019945979,-44.839791068807244]},"properties":{"mapbox": 38}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-13.311854638159275,36.16773976944387]},"properties":{"mapbox": 52}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-114.62683400139213,-54.12948612123728]},"properties":{"mapbox": 60}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-3.5442145820707083,-11.6369563434273]},"properties":{"mapbox": 9}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-86.40065554529428,-89.81849625706673]},"properties":{"mapbox": 21}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-9.042281722649932,-40.06876312661916]},"properties":{"mapbox": 3}},{"type":"Feature","geometry":{"type":"Point","coordinates":[133.63959029316902,54.72414292395115]},"properties":{"mapbox": 17}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-169.43282327614725,-17.5564864045009]},"properties":{"mapbox": 65}},{"type":"Feature","geometry":{"type":"Point","coordinates":[175.429325401783,63.34874477237463]},"properties":{"mapbox": 48}},{"type":"Feature","geometry":{"type":"Point","coordinates":[24.69895239919424,-30.67570684477687]},"properties":{"mapbox": 96}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-9.021967090666294,-2.826137817464769]},"properties":{"mapbox": 44}},{"type":"Feature","geometry":{"type":"Point","coordinates":[80.16331896185875,2.071308419108391]},"properties":{"mapbox": 67}},{"type":"Feature","geometry":{"type":"Point","coordinates":[156.4887184370309,-0.4145095031708479]},"properties":{"mapbox": 43}},{"type":"Feature","geometry":{"type":"Point","coordinates":[56.957034738734365,-2.7943947119638324]},"properties":{"mapbox": 10}},{"type":"Feature","geometry":{"type":"Point","coordinates":[122.7041953895241,40.22421536967158]},"properties":{"mapbox": 16}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-140.48747568391263,50.707125761546195]},"properties":{"mapbox": 16}},{"type":"Feature","geometry":{"type":"Point","coordinates":[143.19000905379653,7.287166477181017]},"properties":{"mapbox": 69}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-10.800317181274295,-53.24537890031934]},"properties":{"mapbox": 23}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-153.7102773785591,-48.76605811994523]},"properties":{"mapbox": 81}},{"type":"Feature","geometry":{"type":"Point","coordinates":[80.65161150880158,-43.97842189762741]},"properties":{"mapbox": 74}},{"type":"Feature","geometry":{"type":"Point","coordinates":[142.3912587761879,83.58667865395546]},"properties":{"mapbox": 64}},{"type":"Feature","geometry":{"type":"Point","coordinates":[150.17928080633283,6.153912632726133]},"properties":{"mapbox": 61}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-64.38352927565575,9.823483773507178]},"properties":{"mapbox": 5}},{"type":"Feature","geometry":{"type":"Point","coordinates":[58.912744307890534,47.091200393624604]},"properties":{"mapbox": 54}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-136.8062863033265,18.77691979520023]},"properties":{"mapbox": 78}},{"type":"Feature","geometry":{"type":"Point","coordinates":[26.39245541766286,-65.10943888686597]},"properties":{"mapbox": 50}},{"type":"Feature","geometry":{"type":"Point","coordinates":[130.17711753956974,-70.88348852936178]},"properties":{"mapbox": 62}},{"type":"Feature","geometry":{"type":"Point","coordinates":[127.53033262677491,36.75111402757466]},"properties":{"mapbox": 75}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-35.52253481000662,7.647080873139203]},"properties":{"mapbox": 42}},{"type":"Feature","geometry":{"type":"Point","coordinates":[128.69959722273052,-51.24652113765478]},"properties":{"mapbox": 99}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-120.66181140951812,74.7534735314548]},"properties":{"mapbox": 7}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-174.26890907809138,-77.67188294325024]},"properties":{"mapbox": 85}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-48.27908635139465,78.52658380754292]},"properties":{"mapbox": 3}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-116.65499053895473,-21.797942728735507]},"properties":{"mapbox": 8}},{"type":"Feature","geometry":{"type":"Point","coordinates":[8.694093273952603,36.89580378122628]},"properties":{"mapbox": 26}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-148.6999091785401,78.27982326038182]},"properties":{"mapbox": 73}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-178.54933228343725,66.6313630901277]},"properties":{"mapbox": 15}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-3.974916497245431,67.34660467598587]},"properties":{"mapbox": 7}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-37.00837626121938,-84.84913899097592]},"properties":{"mapbox": 89}},{"type":"Feature","geometry":{"type":"Point","coordinates":[145.97335637547076,34.95663187466562]},"properties":{"mapbox": 17}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-56.28863143734634,-34.78939819615334]},"properties":{"mapbox": 64}},{"type":"Feature","geometry":{"type":"Point","coordinates":[84.26847699098289,43.814452551305294]},"properties":{"mapbox": 61}},{"type":"Feature","geometry":{"type":"Point","coordinates":[0.9951109439134598,-58.606802369467914]},"properties":{"mapbox": 35}},{"type":"Feature","geometry":{"type":"Point","coordinates":[114.47845210321248,-47.21297385636717]},"properties":{"mapbox": 2}},{"type":"Feature","geometry":{"type":"Point","coordinates":[88.57873197644949,18.16271819639951]},"properties":{"mapbox": 16}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-75.12000326067209,-4.942738856188953]},"properties":{"mapbox": 14}},{"type":"Feature","geometry":{"type":"Point","coordinates":[71.3886886741966,80.58788360562176]},"properties":{"mapbox": 74}},{"type":"Feature","geometry":{"type":"Point","coordinates":[59.52814548276365,-72.08766542840749]},"properties":{"mapbox": 10}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-37.485461784526706,9.892063331790268]},"properties":{"mapbox": 15}},{"type":"Feature","geometry":{"type":"Point","coordinates":[55.82209210842848,-78.70944311376661]},"properties":{"mapbox": 49}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-126.47003570571542,67.87966396193951]},"properties":{"mapbox": 10}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-38.11043333262205,-42.36012026667595]},"properties":{"mapbox": 83}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-68.53293872438371,33.946018600836396]},"properties":{"mapbox": 84}},{"type":"Feature","geometry":{"type":"Point","coordinates":[25.31429372727871,2.216475773602724]},"properties":{"mapbox": 36}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-39.22215071506798,11.108307824470103]},"properties":{"mapbox": 19}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-117.8312218375504,19.109560777433217]},"properties":{"mapbox": 55}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-62.75334340520203,-20.642561917193234]},"properties":{"mapbox": 26}},{"type":"Feature","geometry":{"type":"Point","coordinates":[162.2659510653466,51.33885647635907]},"properties":{"mapbox": 41}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-85.78651492483914,-31.16941594518721]},"properties":{"mapbox": 95}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-107.61159779503942,-62.45351070072502]},"properties":{"mapbox": 72}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-43.02660595625639,2.7774219028651714]},"properties":{"mapbox": 78}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-1.0773403476923704,38.77470628358424]},"properties":{"mapbox": 95}},{"type":"Feature","geometry":{"type":"Point","coordinates":[106.1652319226414,-45.1364218024537]},"properties":{"mapbox": 13}},{"type":"Feature","geometry":{"type":"Point","coordinates":[10.962674310430884,58.15609788056463]},"properties":{"mapbox": 58}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-113.2804127689451,8.134800517000258]},"properties":{"mapbox": 31}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-126.32169186137617,7.1781254187226295]},"properties":{"mapbox": 33}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-141.00995030254126,83.41457965783775]},"properties":{"mapbox": 45}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-32.186284800991416,64.95155287440866]},"properties":{"mapbox": 81}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-93.25948778539896,66.35963486507535]},"properties":{"mapbox": 67}},{"type":"Feature","geometry":{"type":"Point","coordinates":[162.5218812469393,-85.55437384173274]},"properties":{"mapbox": 69}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-35.17443101853132,75.40759117808193]},"properties":{"mapbox": 61}},{"type":"Feature","geometry":{"type":"Point","coordinates":[79.57131528295577,3.550692368298769]},"properties":{"mapbox": 43}},{"type":"Feature","geometry":{"type":"Point","coordinates":[158.92351563088596,-36.40004706569016]},"properties":{"mapbox": 11}},{"type":"Feature","geometry":{"type":"Point","coordinates":[3.597600096836686,78.08964542113245]},"properties":{"mapbox": 19}},{"type":"Feature","geometry":{"type":"Point","coordinates":[137.93049540370703,44.72675796132535]},"properties":{"mapbox": 40}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-139.01084736920893,2.4230836518108845]},"properties":{"mapbox": 3}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-16.42126075923443,82.77291106525809]},"properties":{"mapbox": 7}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-85.56778874248266,-45.06074319127947]},"properties":{"mapbox": 14}},{"type":"Feature","geometry":{"type":"Point","coordinates":[147.66723959706724,74.23324228730053]},"properties":{"mapbox": 10}},{"type":"Feature","geometry":{"type":"Point","coordinates":[9.374368200078607,-12.979612885974348]},"properties":{"mapbox": 91}},{"type":"Feature","geometry":{"type":"Point","coordinates":[153.0515046324581,-52.29332311078906]},"properties":{"mapbox": 9}},{"type":"Feature","geometry":{"type":"Point","coordinates":[162.6496712397784,-44.30713335983455]},"properties":{"mapbox": 15}},{"type":"Feature","geometry":{"type":"Point","coordinates":[100.44096311554313,15.496364515274763]},"properties":{"mapbox": 67}},{"type":"Feature","geometry":{"type":"Point","coordinates":[122.89768223650753,1.9159417133778334]},"properties":{"mapbox": 22}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-153.06688802316785,-51.16702142637223]},"properties":{"mapbox": 58}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-168.30253830179572,31.704005850479007]},"properties":{"mapbox": 77}},{"type":"Feature","geometry":{"type":"Point","coordinates":[170.20371629856527,48.0165243241936]},"properties":{"mapbox": 36}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-118.86547593399882,-45.99883854389191]},"properties":{"mapbox": 21}},{"type":"Feature","geometry":{"type":"Point","coordinates":[109.96864805929363,-28.284501330927014]},"properties":{"mapbox": 5}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-137.61558218859136,-50.10698384139687]},"properties":{"mapbox": 43}},{"type":"Feature","geometry":{"type":"Point","coordinates":[61.364564979448915,-35.452584475278854]},"properties":{"mapbox": 42}},{"type":"Feature","geometry":{"type":"Point","coordinates":[46.31492421962321,-27.288771467283368]},"properties":{"mapbox": 62}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-78.66735175251961,10.934291509911418]},"properties":{"mapbox": 4}},{"type":"Feature","geometry":{"type":"Point","coordinates":[27.444263231009245,28.857922833412886]},"properties":{"mapbox": 12}},{"type":"Feature","geometry":{"type":"Point","coordinates":[174.26482893526554,56.37111263349652]},"properties":{"mapbox": 89}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-52.15299094095826,-37.03383679501712]},"properties":{"mapbox": 21}},{"type":"Feature","geometry":{"type":"Point","coordinates":[157.7483246102929,11.989140948280692]},"properties":{"mapbox": 57}},{"type":"Feature","geometry":{"type":"Point","coordinates":[117.9758988507092,32.22611380275339]},"properties":{"mapbox": 53}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-118.42702779918909,4.662831611931324]},"properties":{"mapbox": 53}},{"type":"Feature","geometry":{"type":"Point","coordinates":[76.23662388883531,-60.47018527518958]},"properties":{"mapbox": 72}},{"type":"Feature","geometry":{"type":"Point","coordinates":[120.13247075490654,-63.97725821938366]},"properties":{"mapbox": 17}},{"type":"Feature","geometry":{"type":"Point","coordinates":[111.46802946925163,-11.89390660263598]},"properties":{"mapbox": 54}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-129.72748928703368,24.851180207915604]},"properties":{"mapbox": 63}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-58.6280823033303,52.36793629825115]},"properties":{"mapbox": 77}},{"type":"Feature","geometry":{"type":"Point","coordinates":[53.84586028754711,45.34744279459119]},"properties":{"mapbox": 7}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-157.346476810053,54.07337267417461]},"properties":{"mapbox": 46}},{"type":"Feature","geometry":{"type":"Point","coordinates":[80.17869346775115,-11.497598988935351]},"properties":{"mapbox": 100}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-84.22597562894225,-15.214260583743453]},"properties":{"mapbox": 27}},{"type":"Feature","geometry":{"type":"Point","coordinates":[140.411637397483,23.820633427239954]},"properties":{"mapbox": 100}},{"type":"Feature","geometry":{"type":"Point","coordinates":[75.10164597071707,-4.103507841937244]},"properties":{"mapbox": 40}},{"type":"Feature","geometry":{"type":"Point","coordinates":[128.12896228395402,-54.10661534406245]},"properties":{"mapbox": 35}},{"type":"Feature","geometry":{"type":"Point","coordinates":[147.15162036940455,34.01164932176471]},"properties":{"mapbox": 93}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-25.507478322833776,60.1441810419783]},"properties":{"mapbox": 17}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-170.80214923247695,87.06924199126661]},"properties":{"mapbox": 1}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-99.63398943655193,-5.895955748856068]},"properties":{"mapbox": 32}},{"type":"Feature","geometry":{"type":"Point","coordinates":[153.87531998567283,-82.07382136490196]},"properties":{"mapbox": 53}},{"type":"Feature","geometry":{"type":"Point","coordinates":[59.5482088252902,83.31037675961852]},"properties":{"mapbox": 28}},{"type":"Feature","geometry":{"type":"Point","coordinates":[169.42099289037287,-44.332350650802255]},"properties":{"mapbox": 67}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-81.0585052985698,84.089150656946]},"properties":{"mapbox": 55}},{"type":"Feature","geometry":{"type":"Point","coordinates":[96.7931511439383,26.270965849980712]},"properties":{"mapbox": 35}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-41.14514362066984,-88.08813147246838]},"properties":{"mapbox": 22}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-119.17482473887503,-64.68080281745642]},"properties":{"mapbox": 26}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-91.66118399240077,55.00636550132185]},"properties":{"mapbox": 25}},{"type":"Feature","geometry":{"type":"Point","coordinates":[26.533783692866564,-10.111068841069937]},"properties":{"mapbox": 64}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-169.00810243561864,-80.94805620610714]},"properties":{"mapbox": 10}},{"type":"Feature","geometry":{"type":"Point","coordinates":[83.92337643541396,-70.91858737170696]},"properties":{"mapbox": 74}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-110.2635882049799,35.70818206295371]},"properties":{"mapbox": 91}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-84.9253267608583,53.84632766246796]},"properties":{"mapbox": 30}},{"type":"Feature","geometry":{"type":"Point","coordinates":[49.118056604638696,33.63964528776705]},"properties":{"mapbox": 97}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-24.056128319352865,-10.252402815967798]},"properties":{"mapbox": 10}},{"type":"Feature","geometry":{"type":"Point","coordinates":[97.68289796076715,30.469459891319275]},"properties":{"mapbox": 74}},{"type":"Feature","geometry":{"type":"Point","coordinates":[74.90200244821608,-9.028823194094002]},"properties":{"mapbox": 90}},{"type":"Feature","geometry":{"type":"Point","coordinates":[152.8573114424944,-57.970860209316015]},"properties":{"mapbox": 98}},{"type":"Feature","geometry":{"type":"Point","coordinates":[44.18907961808145,-16.43768711015582]},"properties":{"mapbox": 5}},{"type":"Feature","geometry":{"type":"Point","coordinates":[148.63756307400763,-84.64647400192916]},"properties":{"mapbox": 71}},{"type":"Feature","geometry":{"type":"Point","coordinates":[65.60330590233207,-49.905973859131336]},"properties":{"mapbox": 12}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-114.47866693139076,16.95708565413952]},"properties":{"mapbox": 73}},{"type":"Feature","geometry":{"type":"Point","coordinates":[82.93869199231267,-27.325535165145993]},"properties":{"mapbox": 21}},{"type":"Feature","geometry":{"type":"Point","coordinates":[66.65229806676507,12.038620957173407]},"properties":{"mapbox": 16}},{"type":"Feature","geometry":{"type":"Point","coordinates":[99.35018842108548,-6.0751106310635805]},"properties":{"mapbox": 77}},{"type":"Feature","geometry":{"type":"Point","coordinates":[164.87748512998223,5.028611663728952]},"properties":{"mapbox": 63}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-174.6840535942465,48.577332738786936]},"properties":{"mapbox": 64}},{"type":"Feature","geometry":{"type":"Point","coordinates":[101.79773608222604,17.904092436656356]},"properties":{"mapbox": 57}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-146.83340367861092,-13.186326962895691]},"properties":{"mapbox": 61}},{"type":"Feature","geometry":{"type":"Point","coordinates":[141.25340058468282,-36.80527599994093]},"properties":{"mapbox": 21}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-97.06560029648244,-2.499908823519945]},"properties":{"mapbox": 56}},{"type":"Feature","geometry":{"type":"Point","coordinates":[20.051825875416398,-28.331027599051595]},"properties":{"mapbox": 6}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-69.09159089438617,8.838886083103716]},"properties":{"mapbox": 74}},{"type":"Feature","geometry":{"type":"Point","coordinates":[122.78145289979875,26.103684855625033]},"properties":{"mapbox": 30}},{"type":"Feature","geometry":{"type":"Point","coordinates":[63.50223418325186,69.0458416286856]},"properties":{"mapbox": 1}},{"type":"Feature","geometry":{"type":"Point","coordinates":[94.53597494401038,-85.31247370876372]},"properties":{"mapbox": 61}},{"type":"Feature","geometry":{"type":"Point","coordinates":[139.0061789844185,-63.92883948516101]},"properties":{"mapbox": 35}},{"type":"Feature","geometry":{"type":"Point","coordinates":[123.51005268283188,20.858707227744162]},"properties":{"mapbox": 25}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-135.57148729451,-86.3604017905891]},"properties":{"mapbox": 43}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-8.634117403998971,-54.98179650865495]},"properties":{"mapbox": 29}},{"type":"Feature","geometry":{"type":"Point","coordinates":[128.89058919623494,17.69125864841044]},"properties":{"mapbox": 73}},{"type":"Feature","geometry":{"type":"Point","coordinates":[51.721485536545515,52.81110931187868]},"properties":{"mapbox": 82}},{"type":"Feature","geometry":{"type":"Point","coordinates":[45.243595289066434,-69.397656256333]},"properties":{"mapbox": 55}},{"type":"Feature","geometry":{"type":"Point","coordinates":[47.58692949078977,36.74179695546627]},"properties":{"mapbox": 51}},{"type":"Feature","geometry":{"type":"Point","coordinates":[1.0598024725914001,-48.50758038461208]},"properties":{"mapbox": 66}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-160.79553843475878,59.84347100369632]},"properties":{"mapbox": 71}},{"type":"Feature","geometry":{"type":"Point","coordinates":[10.428292946889997,79.16157390922308]},"properties":{"mapbox": 46}},{"type":"Feature","geometry":{"type":"Point","coordinates":[46.70798409730196,-31.963963955640793]},"properties":{"mapbox": 12}},{"type":"Feature","geometry":{"type":"Point","coordinates":[80.49403206445277,-54.75363548845053]},"properties":{"mapbox": 1}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-155.00173721462488,36.564332325942814]},"properties":{"mapbox": 86}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-99.90659466013312,-9.391259956173599]},"properties":{"mapbox": 8}},{"type":"Feature","geometry":{"type":"Point","coordinates":[174.40395344048738,-89.39820596482605]},"properties":{"mapbox": 14}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-58.71011330746114,-12.310519167222083]},"properties":{"mapbox": 1}},{"type":"Feature","geometry":{"type":"Point","coordinates":[45.58134910650551,-86.06798916589469]},"properties":{"mapbox": 46}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-118.20363834500313,-0.7904840866103768]},"properties":{"mapbox": 6}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-91.22555846348405,77.6962536200881]},"properties":{"mapbox": 49}},{"type":"Feature","geometry":{"type":"Point","coordinates":[157.6427268795669,-59.78692471515387]},"properties":{"mapbox": 15}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-63.40021602809429,-24.8575499933213]},"properties":{"mapbox": 100}},{"type":"Feature","geometry":{"type":"Point","coordinates":[164.02178032323718,-89.58281000610441]},"properties":{"mapbox": 33}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-47.211816776543856,-88.26625022571534]},"properties":{"mapbox": 61}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-165.34501519985497,22.663344140164554]},"properties":{"mapbox": 20}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-148.6946810502559,74.30929938331246]},"properties":{"mapbox": 51}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-142.7136817574501,23.76247123349458]},"properties":{"mapbox": 48}},{"type":"Feature","geometry":{"type":"Point","coordinates":[48.25156210921705,15.501244836486876]},"properties":{"mapbox": 47}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-151.281230840832,63.81557922810316]},"properties":{"mapbox": 15}},{"type":"Feature","geometry":{"type":"Point","coordinates":[77.62720978818834,53.6626740032807]},"properties":{"mapbox": 26}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-150.78787939622998,41.213500681333244]},"properties":{"mapbox": 51}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-81.1691730748862,4.174361410550773]},"properties":{"mapbox": 81}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-140.40619930252433,23.28936891630292]},"properties":{"mapbox": 98}},{"type":"Feature","geometry":{"type":"Point","coordinates":[64.12042116746306,-70.6528618466109]},"properties":{"mapbox": 51}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-99.25460337661207,-6.878957943990827]},"properties":{"mapbox": 34}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-140.83626553416252,65.52241249009967]},"properties":{"mapbox": 11}},{"type":"Feature","geometry":{"type":"Point","coordinates":[134.3968651536852,-9.85090454109013]},"properties":{"mapbox": 54}},{"type":"Feature","geometry":{"type":"Point","coordinates":[36.84801260009408,35.2313032373786]},"properties":{"mapbox": 79}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-129.0603198017925,40.963220740668476]},"properties":{"mapbox": 56}},{"type":"Feature","geometry":{"type":"Point","coordinates":[150.62101397663355,49.33374434709549]},"properties":{"mapbox": 57}},{"type":"Feature","geometry":{"type":"Point","coordinates":[76.06144060380757,-34.03870543465018]},"properties":{"mapbox": 81}},{"type":"Feature","geometry":{"type":"Point","coordinates":[163.979467805475,46.35060925036669]},"properties":{"mapbox": 22}},{"type":"Feature","geometry":{"type":"Point","coordinates":[150.81318302080035,7.42405473254621]},"properties":{"mapbox": 28}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-29.844722198322415,3.979192692786455]},"properties":{"mapbox": 50}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-52.835115334019065,25.010058763436973]},"properties":{"mapbox": 1}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-75.71951259858906,-73.55637003667653]},"properties":{"mapbox": 47}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-136.67117664590478,-70.3429255541414]},"properties":{"mapbox": 79}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-73.06231426075101,70.98984100855887]},"properties":{"mapbox": 16}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-156.91963417455554,15.910866516642272]},"properties":{"mapbox": 84}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-12.372664399445057,-4.694070084951818]},"properties":{"mapbox": 31}},{"type":"Feature","geometry":{"type":"Point","coordinates":[122.77768355794251,-27.85627781879157]},"properties":{"mapbox": 57}},{"type":"Feature","geometry":{"type":"Point","coordinates":[44.70168672502041,-20.528464056551456]},"properties":{"mapbox": 92}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-12.335470709949732,-23.537044641561806]},"properties":{"mapbox": 81}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-140.60228219255805,58.972157374955714]},"properties":{"mapbox": 15}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-123.32070540636778,-65.65118345897645]},"properties":{"mapbox": 3}},{"type":"Feature","geometry":{"type":"Point","coordinates":[114.25051387399435,2.9149874160066247]},"properties":{"mapbox": 20}},{"type":"Feature","geometry":{"type":"Point","coordinates":[21.60031191073358,86.2766702240333]},"properties":{"mapbox": 58}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-154.44363280199468,-24.90678763948381]},"properties":{"mapbox": 61}},{"type":"Feature","geometry":{"type":"Point","coordinates":[131.86044082045555,-2.055057669058442]},"properties":{"mapbox": 3}},{"type":"Feature","geometry":{"type":"Point","coordinates":[144.8912670928985,70.94839916098863]},"properties":{"mapbox": 38}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-161.79878384806216,88.69376035407186]},"properties":{"mapbox": 95}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-74.18066591955721,-43.07885346002877]},"properties":{"mapbox": 41}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-97.25652420893312,39.15966148953885]},"properties":{"mapbox": 54}},{"type":"Feature","geometry":{"type":"Point","coordinates":[92.06954025663435,51.810072655789554]},"properties":{"mapbox": 15}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-120.58100902475417,86.1243249848485]},"properties":{"mapbox": 96}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-55.17589934170246,87.69860942848027]},"properties":{"mapbox": 11}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-20.77899729833007,87.23163298331201]},"properties":{"mapbox": 45}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-155.208152057603,19.185247686691582]},"properties":{"mapbox": 45}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-142.62498152442276,-88.59571723267436]},"properties":{"mapbox": 69}},{"type":"Feature","geometry":{"type":"Point","coordinates":[2.8595207165926695,-1.6956494143232703]},"properties":{"mapbox": 77}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-61.74546221271157,-21.984181911684573]},"properties":{"mapbox": 12}},{"type":"Feature","geometry":{"type":"Point","coordinates":[38.3966522756964,-35.05400796420872]},"properties":{"mapbox": 9}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-165.4841711372137,42.794102230109274]},"properties":{"mapbox": 36}},{"type":"Feature","geometry":{"type":"Point","coordinates":[31.27043003216386,48.79290644545108]},"properties":{"mapbox": 71}},{"type":"Feature","geometry":{"type":"Point","coordinates":[68.59418503940105,-20.0829499354586]},"properties":{"mapbox": 69}},{"type":"Feature","geometry":{"type":"Point","coordinates":[155.70995614863932,-63.138474840670824]},"properties":{"mapbox": 7}},{"type":"Feature","geometry":{"type":"Point","coordinates":[136.2470793351531,-52.26797728333622]},"properties":{"mapbox": 67}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-18.647782830521464,81.263442998752]},"properties":{"mapbox": 5}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-112.93791395612061,-63.559550140053034]},"properties":{"mapbox": 11}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-176.27190364524722,-74.96294588316232]},"properties":{"mapbox": 16}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-77.52021881751716,-3.193904464133084]},"properties":{"mapbox": 52}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-69.39737948589027,85.30365280341357]},"properties":{"mapbox": 43}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-73.1302220094949,52.12994514964521]},"properties":{"mapbox": 2}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-42.95680331066251,-83.01427349913865]},"properties":{"mapbox": 7}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-124.12824913859367,77.08044554572552]},"properties":{"mapbox": 1}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-15.888626771047711,-68.14556093886495]},"properties":{"mapbox": 80}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-18.11258959583938,85.1007583597675]},"properties":{"mapbox": 25}},{"type":"Feature","geometry":{"type":"Point","coordinates":[73.30335047096014,-18.588879550807178]},"properties":{"mapbox": 27}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-58.48652151413262,34.96230235788971]},"properties":{"mapbox": 34}},{"type":"Feature","geometry":{"type":"Point","coordinates":[87.45559391565621,12.473756019026041]},"properties":{"mapbox": 41}},{"type":"Feature","geometry":{"type":"Point","coordinates":[124.65132314711809,41.32522470317781]},"properties":{"mapbox": 42}},{"type":"Feature","geometry":{"type":"Point","coordinates":[151.07140984386206,24.874209025874734]},"properties":{"mapbox": 75}},{"type":"Feature","geometry":{"type":"Point","coordinates":[102.68346453085542,-8.390642311424017]},"properties":{"mapbox": 6}},{"type":"Feature","geometry":{"type":"Point","coordinates":[148.34359036758542,-42.38129588775337]},"properties":{"mapbox": 76}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-99.5635505206883,70.45818820595741]},"properties":{"mapbox": 40}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-96.98967810720205,10.043996097519994]},"properties":{"mapbox": 87}},{"type":"Feature","geometry":{"type":"Point","coordinates":[159.94566743262112,-59.565283516421914]},"properties":{"mapbox": 95}},{"type":"Feature","geometry":{"type":"Point","coordinates":[47.37261042930186,62.13936241809279]},"properties":{"mapbox": 8}},{"type":"Feature","geometry":{"type":"Point","coordinates":[103.2947247941047,43.08889305219054]},"properties":{"mapbox": 30}},{"type":"Feature","geometry":{"type":"Point","coordinates":[146.88465625047684,-58.113972656428814]},"properties":{"mapbox": 77}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-137.05790324136615,20.781153165735304]},"properties":{"mapbox": 66}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-138.77296476624906,44.03101716656238]},"properties":{"mapbox": 23}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-51.44303695298731,4.003277798183262]},"properties":{"mapbox": 23}},{"type":"Feature","geometry":{"type":"Point","coordinates":[124.53414648771286,14.820579690858722]},"properties":{"mapbox": 94}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-83.0254449415952,-46.82651187758893]},"properties":{"mapbox": 64}},{"type":"Feature","geometry":{"type":"Point","coordinates":[46.23384213075042,-63.285608841106296]},"properties":{"mapbox": 44}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-174.23582612536848,4.970098645426333]},"properties":{"mapbox": 35}},{"type":"Feature","geometry":{"type":"Point","coordinates":[142.85280626267195,-6.949295564554632]},"properties":{"mapbox": 78}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-163.1239470653236,31.5959233045578]},"properties":{"mapbox": 86}},{"type":"Feature","geometry":{"type":"Point","coordinates":[116.5824761800468,-59.94564942084253]},"properties":{"mapbox": 8}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-136.6917859017849,-37.11364797782153]},"properties":{"mapbox": 79}},{"type":"Feature","geometry":{"type":"Point","coordinates":[103.25530093163252,24.097403325140476]},"properties":{"mapbox": 96}},{"type":"Feature","geometry":{"type":"Point","coordinates":[122.77328565716743,-19.82821409124881]},"properties":{"mapbox": 31}},{"type":"Feature","geometry":{"type":"Point","coordinates":[45.46220228075981,-62.11868908256292]},"properties":{"mapbox": 20}},{"type":"Feature","geometry":{"type":"Point","coordinates":[5.600327868014574,29.645729823969305]},"properties":{"mapbox": 79}},{"type":"Feature","geometry":{"type":"Point","coordinates":[176.25345189124346,44.75449895951897]},"properties":{"mapbox": 33}},{"type":"Feature","geometry":{"type":"Point","coordinates":[155.4346466716379,-34.477172805927694]},"properties":{"mapbox": 40}},{"type":"Feature","geometry":{"type":"Point","coordinates":[66.23376390896738,-24.263595174998045]},"properties":{"mapbox": 6}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-78.48930604755878,-2.1970386430621147]},"properties":{"mapbox": 72}},{"type":"Feature","geometry":{"type":"Point","coordinates":[7.033643200993538,5.49671224784106]},"properties":{"mapbox": 16}},{"type":"Feature","geometry":{"type":"Point","coordinates":[25.701569924131036,87.46262247674167]},"properties":{"mapbox": 86}},{"type":"Feature","geometry":{"type":"Point","coordinates":[9.385570446029305,-88.91452769748867]},"properties":{"mapbox": 65}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-78.43776396475732,71.47421572823077]},"properties":{"mapbox": 12}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-132.13995876722038,-24.565173578448594]},"properties":{"mapbox": 74}},{"type":"Feature","geometry":{"type":"Point","coordinates":[85.15281915664673,26.98189042042941]},"properties":{"mapbox": 72}},{"type":"Feature","geometry":{"type":"Point","coordinates":[33.14551834948361,58.590917866677046]},"properties":{"mapbox": 29}},{"type":"Feature","geometry":{"type":"Point","coordinates":[12.777206981554627,-40.59781854506582]},"properties":{"mapbox": 2}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-152.95283705927432,40.74522692710161]},"properties":{"mapbox": 55}},{"type":"Feature","geometry":{"type":"Point","coordinates":[33.83132725954056,35.963861416094005]},"properties":{"mapbox": 69}},{"type":"Feature","geometry":{"type":"Point","coordinates":[30.65096413716674,-44.38926624599844]},"properties":{"mapbox": 46}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-67.66422185115516,-86.67946358677]},"properties":{"mapbox": 17}},{"type":"Feature","geometry":{"type":"Point","coordinates":[146.24046784825623,-26.32830686867237]},"properties":{"mapbox": 92}},{"type":"Feature","geometry":{"type":"Point","coordinates":[161.002056626603,26.446620197966695]},"properties":{"mapbox": 18}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-79.14230517111719,-20.463960911147296]},"properties":{"mapbox": 85}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-157.82514919526875,-10.657458454370499]},"properties":{"mapbox": 87}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-35.3489267360419,-69.7335834056139]},"properties":{"mapbox": 11}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-81.21029569767416,-18.07863819412887]},"properties":{"mapbox": 40}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-151.52670501731336,-44.022110723890364]},"properties":{"mapbox": 51}},{"type":"Feature","geometry":{"type":"Point","coordinates":[12.007898222655058,54.24188781995326]},"properties":{"mapbox": 24}},{"type":"Feature","geometry":{"type":"Point","coordinates":[130.3710228111595,-81.0157336620614]},"properties":{"mapbox": 34}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-81.64349206723273,-26.099831108003855]},"properties":{"mapbox": 11}},{"type":"Feature","geometry":{"type":"Point","coordinates":[148.72055573388934,-54.5143882278353]},"properties":{"mapbox": 46}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-43.217031145468354,48.402493530884385]},"properties":{"mapbox": 4}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-165.64904686063528,0.4130655527114868]},"properties":{"mapbox": 12}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-59.510142747312784,65.23696623276919]},"properties":{"mapbox": 6}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-101.7116868775338,46.655783825553954]},"properties":{"mapbox": 44}},{"type":"Feature","geometry":{"type":"Point","coordinates":[58.53164926171303,14.368595536798239]},"properties":{"mapbox": 11}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-103.56381808407605,39.015411073341966]},"properties":{"mapbox": 49}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-89.27093988284469,-44.17215290945023]},"properties":{"mapbox": 61}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-91.35125365108252,41.45888156723231]},"properties":{"mapbox": 89}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-131.08146680518985,-66.59091998822987]},"properties":{"mapbox": 83}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-117.08627759478986,-16.947918450459838]},"properties":{"mapbox": 16}},{"type":"Feature","geometry":{"type":"Point","coordinates":[153.62486980855465,-53.81671339273453]},"properties":{"mapbox": 81}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-106.19803701527417,-80.1888371212408]},"properties":{"mapbox": 10}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-174.25188459455967,29.692986072041094]},"properties":{"mapbox": 97}},{"type":"Feature","geometry":{"type":"Point","coordinates":[32.50364159233868,-2.496404224075377]},"properties":{"mapbox": 24}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-103.06340957991779,-78.55048308614641]},"properties":{"mapbox": 20}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-108.9317402523011,-60.855275806970894]},"properties":{"mapbox": 12}},{"type":"Feature","geometry":{"type":"Point","coordinates":[55.77836690470576,40.05867382977158]},"properties":{"mapbox": 34}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-162.11817325092852,49.16000903584063]},"properties":{"mapbox": 59}},{"type":"Feature","geometry":{"type":"Point","coordinates":[145.13850834220648,3.7004789896309376]},"properties":{"mapbox": 87}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-62.081177132204175,-21.964824576862156]},"properties":{"mapbox": 11}},{"type":"Feature","geometry":{"type":"Point","coordinates":[20.93774601817131,-53.444322030991316]},"properties":{"mapbox": 85}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-171.9756575860083,-64.89817209541798]},"properties":{"mapbox": 41}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-116.13323274068534,26.10062365885824]},"properties":{"mapbox": 91}},{"type":"Feature","geometry":{"type":"Point","coordinates":[156.13474253565073,34.234340228140354]},"properties":{"mapbox": 29}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-172.49762929044664,51.66065710131079]},"properties":{"mapbox": 79}},{"type":"Feature","geometry":{"type":"Point","coordinates":[145.3119023423642,-9.575124210678041]},"properties":{"mapbox": 87}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-2.450918024405837,88.25738921295851]},"properties":{"mapbox": 34}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-149.6307476889342,20.590436118654907]},"properties":{"mapbox": 86}},{"type":"Feature","geometry":{"type":"Point","coordinates":[31.362647144123912,-4.1990955686196685]},"properties":{"mapbox": 88}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-56.86572044156492,84.52292944304645]},"properties":{"mapbox": 87}},{"type":"Feature","geometry":{"type":"Point","coordinates":[123.03662016056478,66.05640883091837]},"properties":{"mapbox": 5}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-18.58963832259178,76.13934129942209]},"properties":{"mapbox": 53}},{"type":"Feature","geometry":{"type":"Point","coordinates":[144.3702707067132,4.861797667108476]},"properties":{"mapbox": 95}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-82.99937093630433,21.335826530121267]},"properties":{"mapbox": 57}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-7.625324930995703,-20.497307181358337]},"properties":{"mapbox": 42}},{"type":"Feature","geometry":{"type":"Point","coordinates":[97.59205766022205,-55.40064494591206]},"properties":{"mapbox": 59}},{"type":"Feature","geometry":{"type":"Point","coordinates":[21.26893705688417,68.53056010790169]},"properties":{"mapbox": 53}},{"type":"Feature","geometry":{"type":"Point","coordinates":[32.33690533787012,72.49751069582999]},"properties":{"mapbox": 63}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-100.7882856298238,70.50066102761775]},"properties":{"mapbox": 36}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-31.679830932989717,-1.3257330190390348]},"properties":{"mapbox": 77}},{"type":"Feature","geometry":{"type":"Point","coordinates":[159.46228014305234,20.694489609450102]},"properties":{"mapbox": 83}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-28.144683223217726,86.04833926074207]},"properties":{"mapbox": 21}},{"type":"Feature","geometry":{"type":"Point","coordinates":[140.42864402756095,22.618745206855237]},"properties":{"mapbox": 90}},{"type":"Feature","geometry":{"type":"Point","coordinates":[130.53889791481197,-24.182720789685845]},"properties":{"mapbox": 50}},{"type":"Feature","geometry":{"type":"Point","coordinates":[70.73590253479779,-86.99330936651677]},"properties":{"mapbox": 18}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-98.10030215419829,88.10556780081242]},"properties":{"mapbox": 37}},{"type":"Feature","geometry":{"type":"Point","coordinates":[62.98887446522713,48.63231299445033]},"properties":{"mapbox": 51}},{"type":"Feature","geometry":{"type":"Point","coordinates":[15.595593843609095,-36.780918440781534]},"properties":{"mapbox": 69}},{"type":"Feature","geometry":{"type":"Point","coordinates":[51.286226296797395,32.74736411869526]},"properties":{"mapbox": 7}},{"type":"Feature","geometry":{"type":"Point","coordinates":[114.15404026396573,77.14950484223664]},"properties":{"mapbox": 36}},{"type":"Feature","geometry":{"type":"Point","coordinates":[39.96729226782918,-53.55903407558799]},"properties":{"mapbox": 32}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-62.48722493648529,28.135843332856894]},"properties":{"mapbox": 82}},{"type":"Feature","geometry":{"type":"Point","coordinates":[30.14424548484385,16.105412081815302]},"properties":{"mapbox": 77}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-33.47911214455962,-48.06516291573644]},"properties":{"mapbox": 54}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-109.41398383118212,31.07513818424195]},"properties":{"mapbox": 9}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-119.61529349908233,-86.51946256868541]},"properties":{"mapbox": 6}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-66.0242850612849,-82.31715480331331]},"properties":{"mapbox": 69}},{"type":"Feature","geometry":{"type":"Point","coordinates":[165.09211147204041,-38.887647399678826]},"properties":{"mapbox": 64}},{"type":"Feature","geometry":{"type":"Point","coordinates":[144.1246804408729,-75.494344253093]},"properties":{"mapbox": 40}},{"type":"Feature","geometry":{"type":"Point","coordinates":[79.13097124546766,86.41044996678829]},"properties":{"mapbox": 35}},{"type":"Feature","geometry":{"type":"Point","coordinates":[100.04751775413752,88.57521191239357]},"properties":{"mapbox": 53}},{"type":"Feature","geometry":{"type":"Point","coordinates":[2.6331742107868195,-30.016327667981386]},"properties":{"mapbox": 84}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-44.79260975494981,51.192964632064104]},"properties":{"mapbox": 72}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-63.4757920447737,23.448330452665687]},"properties":{"mapbox": 83}},{"type":"Feature","geometry":{"type":"Point","coordinates":[147.41040794178843,74.48687079828233]},"properties":{"mapbox": 72}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-116.04864517226815,-82.21157119609416]},"properties":{"mapbox": 88}},{"type":"Feature","geometry":{"type":"Point","coordinates":[5.459511307999492,-20.853945594280958]},"properties":{"mapbox": 51}},{"type":"Feature","geometry":{"type":"Point","coordinates":[12.284392565488815,-79.69690054189414]},"properties":{"mapbox": 85}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-173.80448911339045,-44.92031320463866]},"properties":{"mapbox": 18}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-57.34907361678779,-23.40254321694374]},"properties":{"mapbox": 27}},{"type":"Feature","geometry":{"type":"Point","coordinates":[85.30275883153081,-57.6118979556486]},"properties":{"mapbox": 83}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-145.31552751548588,7.91812433861196]},"properties":{"mapbox": 82}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-145.0054969266057,-43.26822588220239]},"properties":{"mapbox": 31}},{"type":"Feature","geometry":{"type":"Point","coordinates":[157.0090924669057,74.83082466758788]},"properties":{"mapbox": 54}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-65.92883471399546,-33.93926358316094]},"properties":{"mapbox": 92}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-121.67479659430683,-25.25705936830491]},"properties":{"mapbox": 32}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-6.830966686829925,43.81351998075843]},"properties":{"mapbox": 40}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-24.402449699118733,70.72312654461712]},"properties":{"mapbox": 26}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-159.88104346208274,-35.65545494668186]},"properties":{"mapbox": 32}},{"type":"Feature","geometry":{"type":"Point","coordinates":[116.76082606427372,87.32783841434866]},"properties":{"mapbox": 100}},{"type":"Feature","geometry":{"type":"Point","coordinates":[156.32297242060304,-42.29135069064796]},"properties":{"mapbox": 74}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-24.906591586768627,-70.6086895102635]},"properties":{"mapbox": 68}},{"type":"Feature","geometry":{"type":"Point","coordinates":[41.94615198299289,23.215975589118898]},"properties":{"mapbox": 19}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-75.20145440474153,-36.35918679647148]},"properties":{"mapbox": 7}},{"type":"Feature","geometry":{"type":"Point","coordinates":[3.9644900802522898,20.716046816669405]},"properties":{"mapbox": 57}},{"type":"Feature","geometry":{"type":"Point","coordinates":[11.512293219566345,55.34615373238921]},"properties":{"mapbox": 99}},{"type":"Feature","geometry":{"type":"Point","coordinates":[161.62389552220702,-39.50468803290278]},"properties":{"mapbox": 57}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-121.57353406772017,26.769525934942067]},"properties":{"mapbox": 8}},{"type":"Feature","geometry":{"type":"Point","coordinates":[166.50501578114927,-70.0815476803109]},"properties":{"mapbox": 40}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-72.37302795052528,44.60104311816394]},"properties":{"mapbox": 58}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-134.97790245339274,-9.214969296008348]},"properties":{"mapbox": 16}},{"type":"Feature","geometry":{"type":"Point","coordinates":[108.72965667396784,-20.58310580905527]},"properties":{"mapbox": 67}},{"type":"Feature","geometry":{"type":"Point","coordinates":[157.98479829914868,14.673859756439924]},"properties":{"mapbox": 45}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-94.03324767015874,-4.2099331598728895]},"properties":{"mapbox": 87}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-133.3403992652893,-6.713104136288166]},"properties":{"mapbox": 44}},{"type":"Feature","geometry":{"type":"Point","coordinates":[112.25320626050234,-19.00266712065786]},"properties":{"mapbox": 17}},{"type":"Feature","geometry":{"type":"Point","coordinates":[15.881055146455765,34.15173502173275]},"properties":{"mapbox": 2}},{"type":"Feature","geometry":{"type":"Point","coordinates":[17.096276711672544,-19.413785682991147]},"properties":{"mapbox": 14}},{"type":"Feature","geometry":{"type":"Point","coordinates":[52.863189931958914,41.629943093284965]},"properties":{"mapbox": 77}},{"type":"Feature","geometry":{"type":"Point","coordinates":[12.475525019690394,74.3149668071419]},"properties":{"mapbox": 82}},{"type":"Feature","geometry":{"type":"Point","coordinates":[102.97935325652361,15.563243804499507]},"properties":{"mapbox": 33}},{"type":"Feature","geometry":{"type":"Point","coordinates":[12.641265923157334,-47.30335277505219]},"properties":{"mapbox": 7}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-12.239387277513742,-77.80029118526727]},"properties":{"mapbox": 56}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-40.80227231606841,-15.242899786680937]},"properties":{"mapbox": 35}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-131.24569326639175,31.949481619521976]},"properties":{"mapbox": 44}},{"type":"Feature","geometry":{"type":"Point","coordinates":[114.0491375606507,23.350589415058494]},"properties":{"mapbox": 65}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-176.64725842885673,-43.534041051752865]},"properties":{"mapbox": 2}},{"type":"Feature","geometry":{"type":"Point","coordinates":[131.65887675248086,-64.21674298122525]},"properties":{"mapbox": 61}},{"type":"Feature","geometry":{"type":"Point","coordinates":[21.92272173240781,-81.51891858782619]},"properties":{"mapbox": 49}},{"type":"Feature","geometry":{"type":"Point","coordinates":[138.82551628164947,80.91470603831112]},"properties":{"mapbox": 79}},{"type":"Feature","geometry":{"type":"Point","coordinates":[143.14156810753047,-25.795541242696345]},"properties":{"mapbox": 1}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-166.800395892933,60.18528484739363]},"properties":{"mapbox": 9}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-144.54735475592315,-3.065854632295668]},"properties":{"mapbox": 94}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-150.76181737706065,-83.66180477198213]},"properties":{"mapbox": 80}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-83.05852730758488,-15.419311146251857]},"properties":{"mapbox": 39}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-87.13011137209833,74.085004767403]},"properties":{"mapbox": 47}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-76.42930540256202,-54.16420857422054]},"properties":{"mapbox": 12}},{"type":"Feature","geometry":{"type":"Point","coordinates":[142.2087097261101,44.53137613367289]},"properties":{"mapbox": 4}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-64.55603831447661,34.49843735899776]},"properties":{"mapbox": 94}},{"type":"Feature","geometry":{"type":"Point","coordinates":[42.12144163437188,-11.64163616951555]},"properties":{"mapbox": 87}},{"type":"Feature","geometry":{"type":"Point","coordinates":[85.58279578574002,-14.729487095028162]},"properties":{"mapbox": 88}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-45.583537705242634,28.341183657757938]},"properties":{"mapbox": 61}},{"type":"Feature","geometry":{"type":"Point","coordinates":[3.103248104453087,79.1934456769377]},"properties":{"mapbox": 19}},{"type":"Feature","geometry":{"type":"Point","coordinates":[169.1800141800195,86.2348373606801]},"properties":{"mapbox": 91}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-178.94530529156327,55.29307767748833]},"properties":{"mapbox": 57}},{"type":"Feature","geometry":{"type":"Point","coordinates":[1.3650418817996979,67.20791833475232]},"properties":{"mapbox": 79}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-114.72778706811368,-48.864370845258236]},"properties":{"mapbox": 74}},{"type":"Feature","geometry":{"type":"Point","coordinates":[178.343421863392,64.60342163220048]},"properties":{"mapbox": 32}},{"type":"Feature","geometry":{"type":"Point","coordinates":[177.611748771742,81.19969087187201]},"properties":{"mapbox": 91}},{"type":"Feature","geometry":{"type":"Point","coordinates":[52.74806583300233,-84.27109310869128]},"properties":{"mapbox": 15}},{"type":"Feature","geometry":{"type":"Point","coordinates":[10.081364549696445,-62.88397922180593]},"properties":{"mapbox": 79}},{"type":"Feature","geometry":{"type":"Point","coordinates":[70.66772446967661,-30.233216201886535]},"properties":{"mapbox": 99}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-35.57482288219035,27.087600510567427]},"properties":{"mapbox": 69}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-45.83435098640621,-58.47435358911753]},"properties":{"mapbox": 33}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-20.12691012583673,78.88310491573066]},"properties":{"mapbox": 27}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-19.84980046749115,-73.40256605297327]},"properties":{"mapbox": 44}},{"type":"Feature","geometry":{"type":"Point","coordinates":[99.99531182460487,82.13316347915679]},"properties":{"mapbox": 90}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-40.85276967845857,67.46957691852003]},"properties":{"mapbox": 91}},{"type":"Feature","geometry":{"type":"Point","coordinates":[49.27813838236034,52.89751499891281]},"properties":{"mapbox": 42}},{"type":"Feature","geometry":{"type":"Point","coordinates":[16.61237770691514,69.2438368499279]},"properties":{"mapbox": 73}},{"type":"Feature","geometry":{"type":"Point","coordinates":[108.57693596743047,36.01974913850427]},"properties":{"mapbox": 53}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-100.97691575065255,-76.26019690185785]},"properties":{"mapbox": 91}},{"type":"Feature","geometry":{"type":"Point","coordinates":[85.72310507297516,12.150512491352856]},"properties":{"mapbox": 53}},{"type":"Feature","geometry":{"type":"Point","coordinates":[159.7318111360073,-36.321309520863]},"properties":{"mapbox": 79}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-65.63492445275187,75.05002991762012]},"properties":{"mapbox": 31}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-118.2563142478466,-47.561271605081856]},"properties":{"mapbox": 79}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-124.13570182397962,-13.409972484223545]},"properties":{"mapbox": 100}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-163.29969746991992,43.76724378671497]},"properties":{"mapbox": 68}},{"type":"Feature","geometry":{"type":"Point","coordinates":[25.569858038797975,-26.38374963775277]},"properties":{"mapbox": 2}},{"type":"Feature","geometry":{"type":"Point","coordinates":[179.44557675160468,78.71839796192944]},"properties":{"mapbox": 23}},{"type":"Feature","geometry":{"type":"Point","coordinates":[133.20120753720403,32.78069098480046]},"properties":{"mapbox": 2}},{"type":"Feature","geometry":{"type":"Point","coordinates":[1.894548749551177,19.006769182160497]},"properties":{"mapbox": 89}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-143.3043299149722,38.19644402246922]},"properties":{"mapbox": 27}},{"type":"Feature","geometry":{"type":"Point","coordinates":[127.04680577851832,-29.405142883770168]},"properties":{"mapbox": 99}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-46.50432136841118,-77.19790186733007]},"properties":{"mapbox": 53}},{"type":"Feature","geometry":{"type":"Point","coordinates":[72.4947122298181,-61.52153761591762]},"properties":{"mapbox": 40}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-165.14324208721519,87.08859681151807]},"properties":{"mapbox": 84}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-115.42123725637794,-67.04487129114568]},"properties":{"mapbox": 86}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-114.85377822071314,19.37291443347931]},"properties":{"mapbox": 23}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-145.76720614917576,3.752908087335527]},"properties":{"mapbox": 96}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-36.03865125216544,-39.25180781632662]},"properties":{"mapbox": 94}},{"type":"Feature","geometry":{"type":"Point","coordinates":[57.959803557023406,-9.167421525344253]},"properties":{"mapbox": 52}},{"type":"Feature","geometry":{"type":"Point","coordinates":[169.3040323164314,85.74065003078431]},"properties":{"mapbox": 44}},{"type":"Feature","geometry":{"type":"Point","coordinates":[113.3064625505358,52.742472756654024]},"properties":{"mapbox": 92}},{"type":"Feature","geometry":{"type":"Point","coordinates":[73.00019471906126,-87.96791079919785]},"properties":{"mapbox": 6}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-91.15831162780523,-14.161390447989106]},"properties":{"mapbox": 4}},{"type":"Feature","geometry":{"type":"Point","coordinates":[62.7123543061316,30.70386136416346]},"properties":{"mapbox": 53}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-42.837291955947876,-64.84413576778024]},"properties":{"mapbox": 26}},{"type":"Feature","geometry":{"type":"Point","coordinates":[27.029835870489478,-26.582003817893565]},"properties":{"mapbox": 48}},{"type":"Feature","geometry":{"type":"Point","coordinates":[152.738849921152,-36.81975812651217]},"properties":{"mapbox": 31}},{"type":"Feature","geometry":{"type":"Point","coordinates":[124.25105608999729,-36.163670984096825]},"properties":{"mapbox": 19}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-13.68607610464096,45.78369679860771]},"properties":{"mapbox": 69}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-56.30010978318751,47.8517619241029]},"properties":{"mapbox": 38}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-84.61627434007823,37.964905430562794]},"properties":{"mapbox": 20}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-8.222548104822636,-36.19494575075805]},"properties":{"mapbox": 51}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-175.1437535416335,11.454095025546849]},"properties":{"mapbox": 44}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-108.27845404855907,-3.3510214369744062]},"properties":{"mapbox": 57}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-139.8142277263105,41.52774778660387]},"properties":{"mapbox": 42}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-155.18113357946277,77.3847569944337]},"properties":{"mapbox": 36}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-143.1226884573698,-89.31461127474904]},"properties":{"mapbox": 97}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-15.941828219220042,-76.42533141653985]},"properties":{"mapbox": 94}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-153.1626615766436,-76.40048192348331]},"properties":{"mapbox": 3}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-8.032905869185925,18.317440166138113]},"properties":{"mapbox": 38}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-140.5970814730972,-59.16737722232938]},"properties":{"mapbox": 39}},{"type":"Feature","geometry":{"type":"Point","coordinates":[118.17669169977307,69.35299408156425]},"properties":{"mapbox": 82}}]} diff --git a/debug/site.js b/debug/site.js index 9a0eaa1473e..326893da2ae 100644 --- a/debug/site.js +++ b/debug/site.js @@ -51,7 +51,11 @@ map.on('load', function() { "source": "geojson-random-points", "paint": { "circle-radius": 5, - "circle-color": "#f0f" + "circle-opacity": 0.5, + "circle-color": { + stops: [[0, 'red'], [100, 'violet']], + property: 'mapbox' + } } }); diff --git a/js/data/bucket.js b/js/data/bucket.js index de55f574e2f..fe725b5f321 100644 --- a/js/data/bucket.js +++ b/js/data/bucket.js @@ -76,6 +76,23 @@ function Bucket(options) { this.minZoom = this.layer.minzoom; this.maxZoom = this.layer.maxzoom; + this.createStyleLayer(); + this.attributes = {}; + for (var interfaceName in this.programInterfaces) { + var interfaceAttributes = this.attributes[interfaceName] = { enabled: [], disabled: [] }; + var interface_ = this.programInterfaces[interfaceName]; + for (var i = 0; i < interface_.attributes.length; i++) { + var attribute = interface_.attributes[i]; + if (isAttributeDisabled(this, attribute)) { + interfaceAttributes.disabled.push(util.extend({ + getValue: createGetAttributeValueMethod(this, interfaceName, attribute) + }, attribute)); + } else { + interfaceAttributes.enabled.push(attribute); + } + } + } + if (options.elementGroups) { this.elementGroups = options.elementGroups; this.buffers = util.mapObject(options.arrays, function(array, bufferName) { @@ -86,6 +103,16 @@ function Bucket(options) { } } +function isAttributeDisabled(bucket, attribute) { + if (attribute.isDisabled === undefined || attribute.isDisabled === false) { + return false; + } else if (attribute.isDisabled === true) { + return true; + } else { + return !!attribute.isDisabled.call(bucket); + } +} + /** * Build the buffers! Features are set directly to the `features` property. * @private @@ -147,7 +174,7 @@ Bucket.prototype.createArrays = function() { var vertexAddMethodName = this.getAddMethodName(programName, 'vertex'); var VertexArrayType = new StructArrayType({ - members: programInterface.attributes, + members: this.attributes[programName].enabled, alignment: Buffer.VERTEX_ATTRIBUTE_ALIGNMENT }); @@ -157,7 +184,8 @@ Bucket.prototype.createArrays = function() { this[vertexAddMethodName] = this[vertexAddMethodName] || createVertexAddMethod( programName, programInterface, - this.getBufferName(programName, 'vertex') + this.getBufferName(programName, 'vertex'), + this.attributes[programName].enabled ); } @@ -193,6 +221,28 @@ Bucket.prototype.trimArrays = function() { } }; +/** + * Set the attribute pointers in a WebGL context + * @private + * @param gl The WebGL context + * @param program The active WebGL program + * @param {number} offset The offset of the attribute data in the currently bound GL buffer. + * @param {Array} arguments to be passed to disabled attribute value functions + */ +Bucket.prototype.setAttribPointers = function(programName, gl, program, offset, args) { + // Set disabled attributes + var disabledAttributes = this.attributes[programName].disabled; + for (var i = 0; i < disabledAttributes.length; i++) { + var attribute = disabledAttributes[i]; + var attributeId = program['a_' + attribute.name]; + gl.disableVertexAttribArray(attributeId); + gl['vertexAttrib' + attribute.components + 'fv'](attributeId, attribute.getValue.apply(this, args)); + } + + // Set enabled attributes + this.buffers[this.getBufferName(programName, 'vertex')].setAttribPointers(gl, program, offset); +}; + /** * Get the name of the method used to add an item to a buffer. * @param {string} programName The name of the program that will use the buffer @@ -228,8 +278,10 @@ Bucket.prototype.serialize = function() { }; }; -Bucket.prototype.createStyleLayer = function() { - if (!(this.layer instanceof StyleLayer)) { +Bucket.prototype.createStyleLayer = function(layer) { + if (layer) { + this.layer = layer; + } else if (!(this.layer instanceof StyleLayer)) { this.layer = StyleLayer.create(this.layer); this.layer.updatePaintTransitions([], {transition: false}); this.layer.recalculate(this.zoom, { lastIntegerZoom: Infinity, lastIntegerZoomTime: 0, lastZoom: 0 }); @@ -246,12 +298,12 @@ Bucket.prototype._premultiplyColor = util.premultiply; var createVertexAddMethodCache = {}; -function createVertexAddMethod(programName, programInterface, bufferName) { +function createVertexAddMethod(programName, programInterface, bufferName, enabledAttributes) { var body = ''; var pushArgs = []; - for (var i = 0; i < programInterface.attributes.length; i++) { - var attribute = programInterface.attributes[i]; + for (var i = 0; i < enabledAttributes.length; i++) { + var attribute = enabledAttributes[i]; var attributePushArgs = []; if (Array.isArray(attribute.value)) { @@ -298,7 +350,34 @@ function createElementBufferType(components) { type: Buffer.ELEMENT_ATTRIBUTE_TYPE, name: 'vertices', components: components || 3 - }]}); + }] + }); +} + +var _getAttributeValueCache = {}; +function createGetAttributeValueMethod(bucket, interfaceName, attribute) { + if (!_getAttributeValueCache[interfaceName]) { + _getAttributeValueCache[interfaceName] = {}; + } + + if (!_getAttributeValueCache[interfaceName][attribute.name]) { + var bodyArgs = bucket.programInterfaces[interfaceName].attributeArgs; + var body = 'return '; + + if (Array.isArray(attribute.value)) { + body += '[' + attribute.value.join(', ') + ']'; + } else { + body += attribute.value; + } + + if (attribute.multiplier) { + body += '.map(function(v) { return v * ' + attribute.multiplier + '; })'; + } + + _getAttributeValueCache[interfaceName][attribute.name] = new Function(bodyArgs, body); + } + + return _getAttributeValueCache[interfaceName][attribute.name]; } function capitalize(string) { diff --git a/js/data/bucket/circle_bucket.js b/js/data/bucket/circle_bucket.js index e85c1d129d1..8ec0c78393b 100644 --- a/js/data/bucket/circle_bucket.js +++ b/js/data/bucket/circle_bucket.js @@ -25,7 +25,7 @@ CircleBucket.prototype.programInterfaces = { vertexBuffer: true, elementBuffer: true, - attributeArgs: ['x', 'y', 'extrudeX', 'extrudeY'], + attributeArgs: ['globalProperties', 'featureProperties', 'x', 'y', 'extrudeX', 'extrudeY'], attributes: [{ name: 'pos', @@ -41,25 +41,30 @@ CircleBucket.prototype.programInterfaces = { type: 'Uint8', value: ( 'this._premultiplyColor(' + - 'this.layer.paint["circle-color"],' + - 'this.layer.paint["circle-opacity"]' + + 'this.layer.getPaintValue("circle-color", globalProperties, featureProperties),' + + 'this.layer.getPaintValue("circle-opacity", globalProperties, featureProperties)' + ')' ), - multiplier: 255 + multiplier: 255, + isDisabled: function() { + return ( + this.layer.isPaintValueFeatureConstant("circle-color") && + this.layer.isPaintValueFeatureConstant('circle-opacity') + ); + } }] } }; CircleBucket.prototype.addFeature = function(feature) { + var globalProperties = {zoom: this.zoom}; var geometries = loadGeometry(feature); - for (var j = 0; j < geometries.length; j++) { - var geometry = geometries[j]; - for (var k = 0; k < geometry.length; k++) { - var group = this.makeRoomFor('circle', 4); + for (var j = 0; j < geometries.length; j++) { + for (var k = 0; k < geometries[j].length; k++) { - var x = geometry[k].x; - var y = geometry[k].y; + var x = geometries[j][k].x; + var y = geometries[j][k].y; // Do not include points that are outside the tile boundaries. if (x < 0 || x >= EXTENT || y < 0 || y >= EXTENT) continue; @@ -73,10 +78,12 @@ CircleBucket.prototype.addFeature = function(feature) { // │ 0 1 │ // └─────────┘ - var index = this.addCircleVertex(x, y, -1, -1) - group.vertexStartIndex; - this.addCircleVertex(x, y, 1, -1); - this.addCircleVertex(x, y, 1, 1); - this.addCircleVertex(x, y, -1, 1); + var group = this.makeRoomFor('circle', 4); + + var index = this.addCircleVertex(globalProperties, feature.properties, x, y, -1, -1) - group.vertexStartIndex; + this.addCircleVertex(globalProperties, feature.properties, x, y, 1, -1); + this.addCircleVertex(globalProperties, feature.properties, x, y, 1, 1); + this.addCircleVertex(globalProperties, feature.properties, x, y, -1, 1); group.vertexLength += 4; this.addCircleElement(index, index + 1, index + 2); diff --git a/js/render/draw_circle.js b/js/render/draw_circle.js index eae23ce4811..c8eee47f11a 100644 --- a/js/render/draw_circle.js +++ b/js/render/draw_circle.js @@ -33,6 +33,7 @@ function drawCircles(painter, source, layer, coords) { var tile = source.getTile(coord); var bucket = tile.getBucket(layer); if (!bucket) continue; + bucket.createStyleLayer(layer); var elementGroups = bucket.elementGroups.circle; if (!elementGroups) continue; @@ -52,7 +53,7 @@ function drawCircles(painter, source, layer, coords) { var offset = group.vertexStartIndex * vertex.itemSize; vertex.bind(gl); - vertex.setAttribPointers(gl, program, offset); + bucket.setAttribPointers('circle', gl, program, offset, [{$zoom: painter.transform.zoom}]); elements.bind(gl); diff --git a/js/render/draw_collision_debug.js b/js/render/draw_collision_debug.js index d4775121518..652c4b69abc 100644 --- a/js/render/draw_collision_debug.js +++ b/js/render/draw_collision_debug.js @@ -20,7 +20,7 @@ function drawCollisionDebug(painter, source, layer, coords) { var buffer = bucket.buffers.collisionBoxVertex; buffer.bind(gl); - buffer.setAttribPointers(gl, program, 0); + bucket.setAttribPointers('collisionBox', gl, program, 0); painter.setPosMatrix(coord.posMatrix); diff --git a/js/render/draw_fill.js b/js/render/draw_fill.js index 33b8f7f1909..c375269c607 100644 --- a/js/render/draw_fill.js +++ b/js/render/draw_fill.js @@ -122,7 +122,7 @@ function drawFill(painter, source, layer, coord) { for (var i = 0; i < elementGroups.length; i++) { var group = elementGroups[i]; var offset = group.vertexStartIndex * vertex.itemSize; - vertex.setAttribPointers(gl, fillProgram, offset); + bucket.setAttribPointers('fill', gl, fillProgram, offset); var count = group.elementLength * 3; var elementOffset = group.elementStartIndex * elements.itemSize; @@ -194,7 +194,7 @@ function drawStroke(painter, source, layer, coord) { for (var k = 0; k < elementGroups.length; k++) { var group = elementGroups[k]; var offset = group.vertexStartIndex * vertex.itemSize; - vertex.setAttribPointers(gl, program, offset); + bucket.setAttribPointers('fill', gl, program, offset); var count = group.secondElementLength * 2; var elementOffset = group.secondElementStartIndex * elements.itemSize; diff --git a/js/render/draw_line.js b/js/render/draw_line.js index 7de5875a65d..6792d6ab3a6 100644 --- a/js/render/draw_line.js +++ b/js/render/draw_line.js @@ -170,8 +170,7 @@ module.exports = function drawLine(painter, source, layer, coords) { var group = elementGroups[i]; var vtxOffset = group.vertexStartIndex * vertex.itemSize; - gl.vertexAttribPointer(program.a_pos, 2, gl.SHORT, false, 8, vtxOffset + 0); - gl.vertexAttribPointer(program.a_data, 4, gl.UNSIGNED_BYTE, false, 8, vtxOffset + 4); + bucket.setAttribPointers('line', gl, program, vtxOffset); var count = group.elementLength * 3; var elementOffset = group.elementStartIndex * element.itemSize; diff --git a/js/render/draw_symbol.js b/js/render/draw_symbol.js index 69a2336b7e2..9eb62389967 100644 --- a/js/render/draw_symbol.js +++ b/js/render/draw_symbol.js @@ -71,6 +71,7 @@ function drawSymbol(painter, layer, posMatrix, tile, bucket, elementGroups, pref var gl = painter.gl; posMatrix = painter.translatePosMatrix(posMatrix, tile, layer.paint[prefix + '-translate'], layer.paint[prefix + '-translate-anchor']); + var programInterfaceName = prefix === 'text' ? 'glyph' : 'icon'; var tr = painter.transform; var alignedWithMap = layer.layout[prefix + '-rotation-alignment'] === 'map'; @@ -167,7 +168,7 @@ function drawSymbol(painter, layer, posMatrix, tile, bucket, elementGroups, pref group = elementGroups[j]; offset = group.vertexStartIndex * vertex.itemSize; vertex.bind(gl); - vertex.setAttribPointers(gl, program, offset); + bucket.setAttribPointers(programInterfaceName, gl, program, offset); count = group.elementLength * 3; elementOffset = group.elementStartIndex * elements.itemSize; @@ -184,7 +185,7 @@ function drawSymbol(painter, layer, posMatrix, tile, bucket, elementGroups, pref group = elementGroups[i]; offset = group.vertexStartIndex * vertex.itemSize; vertex.bind(gl); - vertex.setAttribPointers(gl, program, offset); + bucket.setAttribPointers(programInterfaceName, gl, program, offset); count = group.elementLength * 3; elementOffset = group.elementStartIndex * elements.itemSize; @@ -197,7 +198,7 @@ function drawSymbol(painter, layer, posMatrix, tile, bucket, elementGroups, pref group = elementGroups[k]; offset = group.vertexStartIndex * vertex.itemSize; vertex.bind(gl); - vertex.setAttribPointers(gl, program, offset); + bucket.setAttribPointers(programInterfaceName, gl, program, offset); count = group.elementLength * 3; elementOffset = group.elementStartIndex * elements.itemSize; diff --git a/js/style/style_layer.js b/js/style/style_layer.js index 8b3038c5018..1020a8fd21f 100644 --- a/js/style/style_layer.js +++ b/js/style/style_layer.js @@ -165,6 +165,16 @@ StyleLayer.prototype = util.inherit(Evented, { } }, + isPaintValueFeatureConstant: function(name) { + var transition = this._paintTransitions[name]; + + if (transition) { + return transition.declaration.isFeatureConstant; + } else { + return true; + } + }, + isHidden: function(zoom) { if (this.minzoom && zoom < this.minzoom) return true; if (this.maxzoom && zoom >= this.maxzoom) return true; From 1718b3da36c1bad080a2dee7f66eb8326828e9e7 Mon Sep 17 00:00:00 2001 From: Lucas Wojciechowski Date: Tue, 1 Mar 2016 18:06:45 -0800 Subject: [PATCH 07/32] Decouple buffer attribute names from shader attribute names --- js/data/bucket.js | 46 ++++++++++++++++++++++++++++++++++++++--- js/data/buffer.js | 35 +------------------------------ js/util/struct_array.js | 1 - 3 files changed, 44 insertions(+), 38 deletions(-) diff --git a/js/data/bucket.js b/js/data/bucket.js index fe725b5f321..81a7b957092 100644 --- a/js/data/bucket.js +++ b/js/data/bucket.js @@ -88,7 +88,9 @@ function Bucket(options) { getValue: createGetAttributeValueMethod(this, interfaceName, attribute) }, attribute)); } else { - interfaceAttributes.enabled.push(attribute); + interfaceAttributes.enabled.push(util.extend({ + programName: 'a_' + attribute.name + }, attribute)); } } } @@ -221,6 +223,18 @@ Bucket.prototype.trimArrays = function() { } }; +/** + * @enum {string} AttributeType + * @private + * @readonly + */ +var AttributeType = { + Int8: 'BYTE', + Uint8: 'UNSIGNED_BYTE', + Int16: 'SHORT', + Uint16: 'UNSIGNED_SHORT' +}; + /** * Set the attribute pointers in a WebGL context * @private @@ -230,17 +244,43 @@ Bucket.prototype.trimArrays = function() { * @param {Array} arguments to be passed to disabled attribute value functions */ Bucket.prototype.setAttribPointers = function(programName, gl, program, offset, args) { + var attribute; + // Set disabled attributes var disabledAttributes = this.attributes[programName].disabled; for (var i = 0; i < disabledAttributes.length; i++) { - var attribute = disabledAttributes[i]; + attribute = disabledAttributes[i]; var attributeId = program['a_' + attribute.name]; gl.disableVertexAttribArray(attributeId); gl['vertexAttrib' + attribute.components + 'fv'](attributeId, attribute.getValue.apply(this, args)); } // Set enabled attributes - this.buffers[this.getBufferName(programName, 'vertex')].setAttribPointers(gl, program, offset); + var enabledAttributes = this.attributes[programName].enabled; + var vertexBuffer = this.buffers[this.getBufferName(programName, 'vertex')]; + + for (var j = 0; j < enabledAttributes.length; j++) { + attribute = enabledAttributes[j]; + + gl.vertexAttribPointer( + program[attribute.programName], + attribute.components, + gl[AttributeType[attribute.type]], + false, + vertexBuffer.arrayType.bytesPerElement, + offset + getMember(attribute.name).offset + ); + } + + function getMember(memberName) { + var members = vertexBuffer.arrayType.members; + for (var k = 0; k < members.length; k++) { + var member = members[k]; + if (member.name === memberName) { + return member; + } + } + } }; /** diff --git a/js/data/buffer.js b/js/data/buffer.js index eabdb65df65..53092813981 100644 --- a/js/data/buffer.js +++ b/js/data/buffer.js @@ -18,6 +18,7 @@ function Buffer(array, arrayType, type) { this.attributes = arrayType.members; this.itemSize = arrayType.bytesPerElement; this.type = type; + this.arrayType = arrayType; } /** @@ -51,40 +52,6 @@ Buffer.prototype.destroy = function(gl) { } }; -/** - * @enum {string} BufferAttributeType - * @private - * @readonly - */ -var AttributeType = { - Int8: 'BYTE', - Uint8: 'UNSIGNED_BYTE', - Int16: 'SHORT', - Uint16: 'UNSIGNED_SHORT' -}; - -/** - * Set the attribute pointers in a WebGL context according to the buffer's attribute layout - * @private - * @param gl The WebGL context - * @param program The active WebGL program - * @param {number} offset The offset of the attribute data in the currently bound GL buffer. - */ -Buffer.prototype.setAttribPointers = function(gl, program, offset) { - for (var i = 0; i < this.attributes.length; i++) { - var attrib = this.attributes[i]; - - gl.vertexAttribPointer( - program['a_' + attrib.name], - attrib.components, - gl[AttributeType[attrib.type]], - false, - this.itemSize, - offset + attrib.offset - ); - } -}; - /** * @enum {string} BufferType * @private diff --git a/js/util/struct_array.js b/js/util/struct_array.js index 658ec768f62..17582746d5a 100644 --- a/js/util/struct_array.js +++ b/js/util/struct_array.js @@ -316,4 +316,3 @@ StructArray.prototype._refreshViews = function() { this[getArrayViewName(type)] = new viewTypes[type](this.arrayBuffer); } }; - From c5593521f81081f1d63db19b42934f9d23c62cef Mon Sep 17 00:00:00 2001 From: Lucas Wojciechowski Date: Wed, 2 Mar 2016 14:27:36 -0800 Subject: [PATCH 08/32] Get rid of ElementGroup class --- js/data/bucket.js | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/js/data/bucket.js b/js/data/bucket.js index 81a7b957092..0e4483ce795 100644 --- a/js/data/bucket.js +++ b/js/data/bucket.js @@ -147,11 +147,14 @@ Bucket.prototype.makeRoomFor = function(programName, numVertices) { var elementArray = this.arrays[this.getBufferName(programName, 'element')]; var secondElementArray = this.arrays[this.getBufferName(programName, 'secondElement')]; - currentGroup = new ElementGroup( - vertexArray.length, - elementArray && elementArray.length, - secondElementArray && secondElementArray.length - ); + currentGroup = { + vertexStartIndex: vertexArray.length, + elementStartIndex: elementArray && elementArray.length, + secondElementStartIndex: secondElementArray && secondElementArray.length, + elementLength: 0, + vertexLength: 0, + secondElementLength: 0 + }; groups.push(currentGroup); } @@ -423,13 +426,3 @@ function createGetAttributeValueMethod(bucket, interfaceName, attribute) { function capitalize(string) { return string.charAt(0).toUpperCase() + string.slice(1); } - -function ElementGroup(vertexStartIndex, elementStartIndex, secondElementStartIndex) { - // the offset into the vertex buffer of the first vertex in this group - this.vertexStartIndex = vertexStartIndex; - this.elementStartIndex = elementStartIndex; - this.secondElementStartIndex = secondElementStartIndex; - this.elementLength = 0; - this.vertexLength = 0; - this.secondElementLength = 0; -} From d4e68429101b6db6fa704efcc6e5bee123cb6f00 Mon Sep 17 00:00:00 2001 From: Lucas Wojciechowski Date: Wed, 2 Mar 2016 15:19:09 -0800 Subject: [PATCH 09/32] Add buffer offsets to element groups --- js/data/bucket.js | 5 ++++- js/render/draw_circle.js | 9 +++------ js/render/draw_collision_debug.js | 2 +- js/render/draw_fill.js | 12 ++++-------- js/render/draw_line.js | 7 ++----- js/render/draw_symbol.js | 20 +++++++------------- 6 files changed, 21 insertions(+), 34 deletions(-) diff --git a/js/data/bucket.js b/js/data/bucket.js index 0e4483ce795..630353fb6fd 100644 --- a/js/data/bucket.js +++ b/js/data/bucket.js @@ -153,7 +153,10 @@ Bucket.prototype.makeRoomFor = function(programName, numVertices) { secondElementStartIndex: secondElementArray && secondElementArray.length, elementLength: 0, vertexLength: 0, - secondElementLength: 0 + secondElementLength: 0, + elementOffset: elementArray && elementArray.length * elementArray.bytesPerElement, + secondElementOffset: secondElementArray && secondElementArray.length * secondElementArray.bytesPerElement, + vertexOffset: vertexArray && vertexArray.length * vertexArray.bytesPerElement }; groups.push(currentGroup); } diff --git a/js/render/draw_circle.js b/js/render/draw_circle.js index c8eee47f11a..6eeda51e024 100644 --- a/js/render/draw_circle.js +++ b/js/render/draw_circle.js @@ -50,16 +50,13 @@ function drawCircles(painter, source, layer, coords) { for (var k = 0; k < elementGroups.length; k++) { var group = elementGroups[k]; - var offset = group.vertexStartIndex * vertex.itemSize; + var count = group.elementLength * 3; vertex.bind(gl); - bucket.setAttribPointers('circle', gl, program, offset, [{$zoom: painter.transform.zoom}]); - elements.bind(gl); - var count = group.elementLength * 3; - var elementOffset = group.elementStartIndex * elements.itemSize; - gl.drawElements(gl.TRIANGLES, count, gl.UNSIGNED_SHORT, elementOffset); + bucket.setAttribPointers('circle', gl, program, group.vertexOffset, [{$zoom: painter.transform.zoom}]); + gl.drawElements(gl.TRIANGLES, count, gl.UNSIGNED_SHORT, group.elementOffset); } } diff --git a/js/render/draw_collision_debug.js b/js/render/draw_collision_debug.js index 652c4b69abc..e6ab1d0d117 100644 --- a/js/render/draw_collision_debug.js +++ b/js/render/draw_collision_debug.js @@ -20,7 +20,7 @@ function drawCollisionDebug(painter, source, layer, coords) { var buffer = bucket.buffers.collisionBoxVertex; buffer.bind(gl); - bucket.setAttribPointers('collisionBox', gl, program, 0); + bucket.setAttribPointers('collisionBox', gl, program, elementGroups[0].vertexOffset); painter.setPosMatrix(coord.posMatrix); diff --git a/js/render/draw_fill.js b/js/render/draw_fill.js index c375269c607..40d82ed9630 100644 --- a/js/render/draw_fill.js +++ b/js/render/draw_fill.js @@ -121,12 +121,10 @@ function drawFill(painter, source, layer, coord) { for (var i = 0; i < elementGroups.length; i++) { var group = elementGroups[i]; - var offset = group.vertexStartIndex * vertex.itemSize; - bucket.setAttribPointers('fill', gl, fillProgram, offset); + bucket.setAttribPointers('fill', gl, fillProgram, group.vertexOffset); var count = group.elementLength * 3; - var elementOffset = group.elementStartIndex * elements.itemSize; - gl.drawElements(gl.TRIANGLES, count, gl.UNSIGNED_SHORT, elementOffset); + gl.drawElements(gl.TRIANGLES, count, gl.UNSIGNED_SHORT, group.elementOffset); } // Now that we have the stencil mask in the stencil buffer, we can start @@ -193,12 +191,10 @@ function drawStroke(painter, source, layer, coord) { for (var k = 0; k < elementGroups.length; k++) { var group = elementGroups[k]; - var offset = group.vertexStartIndex * vertex.itemSize; - bucket.setAttribPointers('fill', gl, program, offset); + bucket.setAttribPointers('fill', gl, program, group.vertexOffset); var count = group.secondElementLength * 2; - var elementOffset = group.secondElementStartIndex * elements.itemSize; - gl.drawElements(gl.LINES, count, gl.UNSIGNED_SHORT, elementOffset); + gl.drawElements(gl.LINES, count, gl.UNSIGNED_SHORT, group.secondElementOffset); } } diff --git a/js/render/draw_line.js b/js/render/draw_line.js index 6792d6ab3a6..027b8035dae 100644 --- a/js/render/draw_line.js +++ b/js/render/draw_line.js @@ -168,13 +168,10 @@ module.exports = function drawLine(painter, source, layer, coords) { for (var i = 0; i < elementGroups.length; i++) { var group = elementGroups[i]; - - var vtxOffset = group.vertexStartIndex * vertex.itemSize; - bucket.setAttribPointers('line', gl, program, vtxOffset); + bucket.setAttribPointers('line', gl, program, group.vertexOffset); var count = group.elementLength * 3; - var elementOffset = group.elementStartIndex * element.itemSize; - gl.drawElements(gl.TRIANGLES, count, gl.UNSIGNED_SHORT, elementOffset); + gl.drawElements(gl.TRIANGLES, count, gl.UNSIGNED_SHORT, group.elementOffset); } } diff --git a/js/render/draw_symbol.js b/js/render/draw_symbol.js index 9eb62389967..82a203a4afd 100644 --- a/js/render/draw_symbol.js +++ b/js/render/draw_symbol.js @@ -146,7 +146,7 @@ function drawSymbol(painter, layer, posMatrix, tile, bucket, elementGroups, pref painter.frameHistory.bind(gl); gl.uniform1i(program.u_fadetexture, 1); - var group, offset, count, elementOffset; + var group, count; elements.bind(gl); @@ -166,13 +166,11 @@ function drawSymbol(painter, layer, posMatrix, tile, bucket, elementGroups, pref for (var j = 0; j < elementGroups.length; j++) { group = elementGroups[j]; - offset = group.vertexStartIndex * vertex.itemSize; vertex.bind(gl); - bucket.setAttribPointers(programInterfaceName, gl, program, offset); + bucket.setAttribPointers(programInterfaceName, gl, program, group.vertexOffset); count = group.elementLength * 3; - elementOffset = group.elementStartIndex * elements.itemSize; - gl.drawElements(gl.TRIANGLES, count, gl.UNSIGNED_SHORT, elementOffset); + gl.drawElements(gl.TRIANGLES, count, gl.UNSIGNED_SHORT, group.elementOffset); } } @@ -183,26 +181,22 @@ function drawSymbol(painter, layer, posMatrix, tile, bucket, elementGroups, pref for (var i = 0; i < elementGroups.length; i++) { group = elementGroups[i]; - offset = group.vertexStartIndex * vertex.itemSize; vertex.bind(gl); - bucket.setAttribPointers(programInterfaceName, gl, program, offset); + bucket.setAttribPointers(programInterfaceName, gl, program, group.vertexOffset); count = group.elementLength * 3; - elementOffset = group.elementStartIndex * elements.itemSize; - gl.drawElements(gl.TRIANGLES, count, gl.UNSIGNED_SHORT, elementOffset); + gl.drawElements(gl.TRIANGLES, count, gl.UNSIGNED_SHORT, group.elementOffset); } } else { gl.uniform1f(program.u_opacity, layer.paint['icon-opacity']); for (var k = 0; k < elementGroups.length; k++) { group = elementGroups[k]; - offset = group.vertexStartIndex * vertex.itemSize; vertex.bind(gl); - bucket.setAttribPointers(programInterfaceName, gl, program, offset); + bucket.setAttribPointers(programInterfaceName, gl, program, group.vertexOffset); count = group.elementLength * 3; - elementOffset = group.elementStartIndex * elements.itemSize; - gl.drawElements(gl.TRIANGLES, count, gl.UNSIGNED_SHORT, elementOffset); + gl.drawElements(gl.TRIANGLES, count, gl.UNSIGNED_SHORT, group.elementOffset); } } } From 14d0cd71590ba461ec509fa185d272916ab86b25 Mon Sep 17 00:00:00 2001 From: Lucas Wojciechowski Date: Wed, 2 Mar 2016 15:02:42 -0800 Subject: [PATCH 10/32] Use Bucket#bindBuffers instead of Buffer#bind in draw methods --- js/data/bucket.js | 19 +++++++++++++++++++ js/render/draw_circle.js | 8 +------- js/render/draw_collision_debug.js | 3 +-- js/render/draw_fill.js | 12 ++---------- js/render/draw_line.js | 5 +---- js/render/draw_symbol.js | 12 ++---------- 6 files changed, 26 insertions(+), 33 deletions(-) diff --git a/js/data/bucket.js b/js/data/bucket.js index 630353fb6fd..43509d97298 100644 --- a/js/data/bucket.js +++ b/js/data/bucket.js @@ -289,6 +289,25 @@ Bucket.prototype.setAttribPointers = function(programName, gl, program, offset, } }; +Bucket.prototype.bindBuffers = function(programInterfaceName, gl, options) { + var programInterface = this.programInterfaces[programInterfaceName]; + + if (programInterface.vertexBuffer) { + var vertexBuffer = this.buffers[this.getBufferName(programInterfaceName, 'vertex')]; + vertexBuffer.bind(gl); + } + + if (programInterface.elementBuffer && (!options || !options.secondElement)) { + var elementBuffer = this.buffers[this.getBufferName(programInterfaceName, 'element')]; + elementBuffer.bind(gl); + } + + if (programInterface.secondElementBuffer && (options && options.secondElement)) { + var secondElementBuffer = this.buffers[this.getBufferName(programInterfaceName, 'secondElement')]; + secondElementBuffer.bind(gl); + } +}; + /** * Get the name of the method used to add an item to a buffer. * @param {string} programName The name of the program that will use the buffer diff --git a/js/render/draw_circle.js b/js/render/draw_circle.js index 6eeda51e024..7d7effca8cf 100644 --- a/js/render/draw_circle.js +++ b/js/render/draw_circle.js @@ -37,9 +37,6 @@ function drawCircles(painter, source, layer, coords) { var elementGroups = bucket.elementGroups.circle; if (!elementGroups) continue; - var vertex = bucket.buffers.circleVertex; - var elements = bucket.buffers.circleElement; - painter.setPosMatrix(painter.translatePosMatrix( coord.posMatrix, tile, @@ -48,13 +45,10 @@ function drawCircles(painter, source, layer, coords) { )); painter.setExMatrix(painter.transform.exMatrix); + bucket.bindBuffers('circle', gl); for (var k = 0; k < elementGroups.length; k++) { var group = elementGroups[k]; var count = group.elementLength * 3; - - vertex.bind(gl); - elements.bind(gl); - bucket.setAttribPointers('circle', gl, program, group.vertexOffset, [{$zoom: painter.transform.zoom}]); gl.drawElements(gl.TRIANGLES, count, gl.UNSIGNED_SHORT, group.elementOffset); } diff --git a/js/render/draw_collision_debug.js b/js/render/draw_collision_debug.js index e6ab1d0d117..3737102249d 100644 --- a/js/render/draw_collision_debug.js +++ b/js/render/draw_collision_debug.js @@ -18,8 +18,7 @@ function drawCollisionDebug(painter, source, layer, coords) { if (!bucket.buffers) continue; if (elementGroups[0].vertexLength === 0) continue; - var buffer = bucket.buffers.collisionBoxVertex; - buffer.bind(gl); + bucket.bindBuffers('collisionBox', gl); bucket.setAttribPointers('collisionBox', gl, program, elementGroups[0].vertexOffset); painter.setPosMatrix(coord.posMatrix); diff --git a/js/render/draw_fill.js b/js/render/draw_fill.js index 40d82ed9630..b22084b0c71 100644 --- a/js/render/draw_fill.js +++ b/js/render/draw_fill.js @@ -112,12 +112,7 @@ function drawFill(painter, source, layer, coord) { // Draw the actual triangle fan into the stencil buffer. var fillProgram = painter.useProgram('fill', translatedPosMatrix); - // Draw all buffers - var vertex = bucket.buffers.fillVertex; - vertex.bind(gl); - - var elements = bucket.buffers.fillElement; - elements.bind(gl); + bucket.bindBuffers('fill', gl); for (var i = 0; i < elementGroups.length; i++) { var group = elementGroups[i]; @@ -182,10 +177,7 @@ function drawStroke(painter, source, layer, coord) { if (image) { setPattern(image, opacity, tile, coord, painter, program); } // Draw all buffers - var vertex = bucket.buffers.fillVertex; - var elements = bucket.buffers.fillSecondElement; - vertex.bind(gl); - elements.bind(gl); + bucket.bindBuffers('fill', gl, {secondElement: true}); painter.enableTileClippingMask(coord); diff --git a/js/render/draw_line.js b/js/render/draw_line.js index 027b8035dae..14ee9fc1573 100644 --- a/js/render/draw_line.js +++ b/js/render/draw_line.js @@ -161,10 +161,7 @@ module.exports = function drawLine(painter, source, layer, coords) { gl.uniform1f(program.u_ratio, ratio); } - var vertex = bucket.buffers.lineVertex; - vertex.bind(gl); - var element = bucket.buffers.lineElement; - element.bind(gl); + bucket.bindBuffers('line', gl); for (var i = 0; i < elementGroups.length; i++) { var group = elementGroups[i]; diff --git a/js/render/draw_symbol.js b/js/render/draw_symbol.js index 82a203a4afd..9b7f2fd7080 100644 --- a/js/render/draw_symbol.js +++ b/js/render/draw_symbol.js @@ -100,7 +100,6 @@ function drawSymbol(painter, layer, posMatrix, tile, bucket, elementGroups, pref var extra = (topedgelength + x) / topedgelength - 1; var text = prefix === 'text'; - var vertex, elements; if (!text && !painter.style.sprite.loaded()) return; @@ -118,16 +117,12 @@ function drawSymbol(painter, layer, posMatrix, tile, bucket, elementGroups, pref if (!glyphAtlas) return; glyphAtlas.updateTexture(gl); - vertex = bucket.buffers.glyphVertex; - elements = bucket.buffers.glyphElement; texsize = [glyphAtlas.width / 4, glyphAtlas.height / 4]; } else { var mapMoving = painter.options.rotating || painter.options.zooming; var iconScaled = fontScale !== 1 || browser.devicePixelRatio !== painter.spriteAtlas.pixelRatio || iconsNeedLinear; var iconTransformed = alignedWithMap || painter.transform.pitch; painter.spriteAtlas.bind(gl, sdf || mapMoving || iconScaled || iconTransformed); - vertex = bucket.buffers.iconVertex; - elements = bucket.buffers.iconElement; texsize = [painter.spriteAtlas.width / 4, painter.spriteAtlas.height / 4]; } @@ -148,7 +143,7 @@ function drawSymbol(painter, layer, posMatrix, tile, bucket, elementGroups, pref var group, count; - elements.bind(gl); + bucket.bindBuffers(programInterfaceName, gl); if (sdf) { var sdfPx = 8; @@ -166,9 +161,7 @@ function drawSymbol(painter, layer, posMatrix, tile, bucket, elementGroups, pref for (var j = 0; j < elementGroups.length; j++) { group = elementGroups[j]; - vertex.bind(gl); bucket.setAttribPointers(programInterfaceName, gl, program, group.vertexOffset); - count = group.elementLength * 3; gl.drawElements(gl.TRIANGLES, count, gl.UNSIGNED_SHORT, group.elementOffset); } @@ -181,7 +174,7 @@ function drawSymbol(painter, layer, posMatrix, tile, bucket, elementGroups, pref for (var i = 0; i < elementGroups.length; i++) { group = elementGroups[i]; - vertex.bind(gl); + bucket.bindBuffers(programInterfaceName, gl); bucket.setAttribPointers(programInterfaceName, gl, program, group.vertexOffset); count = group.elementLength * 3; @@ -192,7 +185,6 @@ function drawSymbol(painter, layer, posMatrix, tile, bucket, elementGroups, pref gl.uniform1f(program.u_opacity, layer.paint['icon-opacity']); for (var k = 0; k < elementGroups.length; k++) { group = elementGroups[k]; - vertex.bind(gl); bucket.setAttribPointers(programInterfaceName, gl, program, group.vertexOffset); count = group.elementLength * 3; From 64d20ae14f3ef24cd6369a8f66f9bf7413f5524e Mon Sep 17 00:00:00 2001 From: Lucas Wojciechowski Date: Thu, 3 Mar 2016 14:16:02 -0800 Subject: [PATCH 11/32] Create StyleLayer instances for all child layers --- js/data/bucket.js | 33 ++++++++++++++++++++---------- js/data/bucket/symbol_bucket.js | 5 ----- js/render/draw_circle.js | 2 +- js/source/worker_tile.js | 23 +++++++++++++-------- test/js/data/bucket.test.js | 2 +- test/js/data/fill_bucket.test.js | 4 +++- test/js/data/line_bucket.test.js | 4 +++- test/js/data/symbol_bucket.test.js | 4 +++- 8 files changed, 47 insertions(+), 30 deletions(-) diff --git a/js/data/bucket.js b/js/data/bucket.js index 43509d97298..0c776a4efee 100644 --- a/js/data/bucket.js +++ b/js/data/bucket.js @@ -65,8 +65,8 @@ function Bucket(options) { this.zoom = options.zoom; this.overscaling = options.overscaling; this.layer = options.layer; + this.childLayers = options.childLayers; - this.layerIDs = [this.layer.id]; this.type = this.layer.type; this.features = []; this.id = this.layer.id; @@ -76,7 +76,7 @@ function Bucket(options) { this.minZoom = this.layer.minzoom; this.maxZoom = this.layer.maxzoom; - this.createStyleLayer(); + this.createStyleLayers(); this.attributes = {}; for (var interfaceName in this.programInterfaces) { var interfaceAttributes = this.attributes[interfaceName] = { enabled: [], disabled: [] }; @@ -120,7 +120,6 @@ function isAttributeDisabled(bucket, attribute) { * @private */ Bucket.prototype.populateBuffers = function() { - this.createStyleLayer(); this.createArrays(); for (var i = 0; i < this.features.length; i++) { @@ -339,17 +338,29 @@ Bucket.prototype.serialize = function() { arrays: util.mapObject(this.arrays, function(array) { return array.serialize(); }), - arrayTypes: this.arrayTypes + arrayTypes: this.arrayTypes, + childLayers: this.childLayers.map(function(layer) { + return { id: layer.id }; + }) }; }; -Bucket.prototype.createStyleLayer = function(layer) { - if (layer) { - this.layer = layer; - } else if (!(this.layer instanceof StyleLayer)) { - this.layer = StyleLayer.create(this.layer); - this.layer.updatePaintTransitions([], {transition: false}); - this.layer.recalculate(this.zoom, { lastIntegerZoom: Infinity, lastIntegerZoomTime: 0, lastZoom: 0 }); +Bucket.prototype.createStyleLayers = function(style) { + var that = this; + var refLayer = this.layer = create(this.layer); + this.childLayers = this.childLayers.map(create); + + function create(layer) { + if (style) { + return style.getLayer(layer.id); + } else if (!(layer instanceof StyleLayer)) { + layer = StyleLayer.create(layer, refLayer); + layer.updatePaintTransitions({}, {transition: false}); + layer.recalculate(that.zoom, { lastIntegerZoom: Infinity, lastIntegerZoomTime: 0, lastZoom: 0 }); + return layer; + } else { + return layer; + } } }; diff --git a/js/data/bucket/symbol_bucket.js b/js/data/bucket/symbol_bucket.js index e5036a3da7c..443baf0ca5c 100644 --- a/js/data/bucket/symbol_bucket.js +++ b/js/data/bucket/symbol_bucket.js @@ -115,7 +115,6 @@ SymbolBucket.prototype.programInterfaces = { }; SymbolBucket.prototype.populateBuffers = function(collisionTile, stacks, icons) { - this.createStyleLayer(); // To reduce the number of labels that jump around when zooming we need // to use a text-size value that is the same for all zoom levels. @@ -479,8 +478,6 @@ SymbolBucket.prototype.addSymbols = function(programName, quads, scale, keepUpri }; SymbolBucket.prototype.updateIcons = function(icons) { - this.createStyleLayer(); - var iconValue = this.layer.layout['icon-image']; if (!iconValue) return; @@ -492,8 +489,6 @@ SymbolBucket.prototype.updateIcons = function(icons) { }; SymbolBucket.prototype.updateFont = function(stacks) { - this.createStyleLayer(); - var fontName = this.layer.layout['text-font'], stack = stacks[fontName] = stacks[fontName] || {}; diff --git a/js/render/draw_circle.js b/js/render/draw_circle.js index 7d7effca8cf..553afa70a25 100644 --- a/js/render/draw_circle.js +++ b/js/render/draw_circle.js @@ -33,7 +33,7 @@ function drawCircles(painter, source, layer, coords) { var tile = source.getTile(coord); var bucket = tile.getBucket(layer); if (!bucket) continue; - bucket.createStyleLayer(layer); + bucket.createStyleLayers(painter.style); var elementGroups = bucket.elementGroups.circle; if (!elementGroups) continue; diff --git a/js/source/worker_tile.js b/js/source/worker_tile.js index a1056b596de..f05cbf55eed 100644 --- a/js/source/worker_tile.js +++ b/js/source/worker_tile.js @@ -40,6 +40,14 @@ WorkerTile.prototype.parse = function(data, layers, actor, rawTileData, callback var sourceLayerId; var bucket; + var childLayers = {}; + for (i = 0; i < layers.length; i++) { + layer = layers[i]; + var key = layer.ref || layer.id; + childLayers[key] = childLayers[key] || []; + childLayers[key].push(layer); + } + // Map non-ref layers to buckets. for (i = 0; i < layers.length; i++) { layer = layers[i]; @@ -54,6 +62,7 @@ WorkerTile.prototype.parse = function(data, layers, actor, rawTileData, callback bucket = Bucket.create({ layer: layer, index: i, + childLayers: childLayers[layer.id], zoom: this.zoom, overscaling: this.overscaling, showCollisionBoxes: this.showCollisionBoxes, @@ -71,14 +80,6 @@ WorkerTile.prototype.parse = function(data, layers, actor, rawTileData, callback } } - // Index ref layers. - for (i = 0; i < layers.length; i++) { - layer = layers[i]; - if (layer.source === this.source && layer.ref && bucketsById[layer.ref]) { - bucketsById[layer.ref].layerIDs.push(layer.id); - } - } - // read each layer, and sort its features into buckets if (data.layers) { // vectortile for (sourceLayerId in bucketsBySourceLayer) { @@ -112,7 +113,7 @@ WorkerTile.prototype.parse = function(data, layers, actor, rawTileData, callback bucket = bucketsById[id]; if (bucket.features.length === 0) continue; - featureIndex.bucketLayerIDs[bucket.index] = bucket.layerIDs; + featureIndex.bucketLayerIDs[bucket.index] = bucket.childLayers.map(getLayerId); buckets.push(bucket); @@ -269,3 +270,7 @@ function getTransferables(buckets) { } return transferables; } + +function getLayerId(layer) { + return layer.id; +} diff --git a/test/js/data/bucket.test.js b/test/js/data/bucket.test.js index 77373903df9..c463132ca06 100644 --- a/test/js/data/bucket.test.js +++ b/test/js/data/bucket.test.js @@ -58,6 +58,7 @@ test('Bucket', function(t) { var Class = createClass(); return new Class({ layer: { type: 'circle' }, + childLayers: [{ type: 'circle' }], buffers: {} }); } @@ -142,7 +143,6 @@ test('Bucket', function(t) { t.test('layout properties', function(t) { var bucket = create(); - bucket.createStyleLayer(); t.equal(bucket.layer.layout.visibility, 'visible'); t.end(); }); diff --git a/test/js/data/fill_bucket.test.js b/test/js/data/fill_bucket.test.js index 351dc400330..e4a2e322875 100644 --- a/test/js/data/fill_bucket.test.js +++ b/test/js/data/fill_bucket.test.js @@ -17,9 +17,11 @@ test('FillBucket', function(t) { var warn = console.warn; console.warn = function() {}; + var layer = { id: 'test', type: 'fill', layout: {} }; var bucket = new FillBucket({ buffers: {}, - layer: { id: 'test', type: 'fill', layout: {} } + layer: layer, + childLayers: [layer] }); bucket.createArrays(); diff --git a/test/js/data/line_bucket.test.js b/test/js/data/line_bucket.test.js index b48a59da930..a4110f82e0b 100644 --- a/test/js/data/line_bucket.test.js +++ b/test/js/data/line_bucket.test.js @@ -13,9 +13,11 @@ var vt = new VectorTile(new Protobuf(new Uint8Array(fs.readFileSync(path.join(__ var feature = vt.layers.road.feature(0); test('LineBucket', function(t) { + var layer = { id: 'test', type: 'line', layout: {} }; var bucket = new LineBucket({ buffers: {}, - layer: { id: 'test', type: 'line', layout: {} } + layer: layer, + childLayers: [layer] }); bucket.createArrays(); diff --git a/test/js/data/symbol_bucket.test.js b/test/js/data/symbol_bucket.test.js index 573c122844b..4f67dbde099 100644 --- a/test/js/data/symbol_bucket.test.js +++ b/test/js/data/symbol_bucket.test.js @@ -29,12 +29,14 @@ test('SymbolBucket', function(t) { var stacks = { 'Test': glyphs }; function bucketSetup() { + var layer = { id: 'test', type: 'symbol', layout: {'text-font': ['Test'] }}; var bucket = new SymbolBucket({ buffers: buffers, overscaling: 1, zoom: 0, collisionBoxArray: collisionBoxArray, - layer: { id: 'test', type: 'symbol', layout: {'text-font': ['Test'] }}, + layer: layer, + childLayers: [layer], tileExtent: 4096 }); bucket.createArrays(); From 009a6978f0b5ed41379cae48b15990f8f54510e7 Mon Sep 17 00:00:00 2001 From: Lucas Wojciechowski Date: Thu, 3 Mar 2016 14:24:33 -0800 Subject: [PATCH 12/32] Support ref families --- js/data/bucket.js | 120 ++++++++++++++++++------------ js/data/bucket/circle_bucket.js | 11 +-- js/render/draw_circle.js | 2 +- js/render/draw_collision_debug.js | 2 +- js/render/draw_fill.js | 4 +- js/render/draw_line.js | 2 +- js/render/draw_symbol.js | 7 +- test/js/data/bucket.test.js | 16 ++-- 8 files changed, 94 insertions(+), 70 deletions(-) diff --git a/js/data/bucket.js b/js/data/bucket.js index 0c776a4efee..fe0f036d8fd 100644 --- a/js/data/bucket.js +++ b/js/data/bucket.js @@ -77,23 +77,7 @@ function Bucket(options) { this.maxZoom = this.layer.maxzoom; this.createStyleLayers(); - this.attributes = {}; - for (var interfaceName in this.programInterfaces) { - var interfaceAttributes = this.attributes[interfaceName] = { enabled: [], disabled: [] }; - var interface_ = this.programInterfaces[interfaceName]; - for (var i = 0; i < interface_.attributes.length; i++) { - var attribute = interface_.attributes[i]; - if (isAttributeDisabled(this, attribute)) { - interfaceAttributes.disabled.push(util.extend({ - getValue: createGetAttributeValueMethod(this, interfaceName, attribute) - }, attribute)); - } else { - interfaceAttributes.enabled.push(util.extend({ - programName: 'a_' + attribute.name - }, attribute)); - } - } - } + this.attributes = createAttributes(this); if (options.elementGroups) { this.elementGroups = options.elementGroups; @@ -105,16 +89,6 @@ function Bucket(options) { } } -function isAttributeDisabled(bucket, attribute) { - if (attribute.isDisabled === undefined || attribute.isDisabled === false) { - return false; - } else if (attribute.isDisabled === true) { - return true; - } else { - return !!attribute.isDisabled.call(bucket); - } -} - /** * Build the buffers! Features are set directly to the `features` property. * @private @@ -177,8 +151,8 @@ Bucket.prototype.createArrays = function() { var programInterface = this.programInterfaces[programName]; if (programInterface.vertexBuffer) { - var vertexBufferName = this.getBufferName(programName, 'vertex'); var vertexAddMethodName = this.getAddMethodName(programName, 'vertex'); + var vertexBufferName = this.getBufferName(programName, 'vertex'); var VertexArrayType = new StructArrayType({ members: this.attributes[programName].enabled, @@ -188,12 +162,7 @@ Bucket.prototype.createArrays = function() { arrays[vertexBufferName] = new VertexArrayType(); arrayTypes[vertexBufferName] = VertexArrayType.serialize(); - this[vertexAddMethodName] = this[vertexAddMethodName] || createVertexAddMethod( - programName, - programInterface, - this.getBufferName(programName, 'vertex'), - this.attributes[programName].enabled - ); + this[vertexAddMethodName] = this[vertexAddMethodName] || createVertexAddMethod(this, programName); } if (programInterface.elementBuffer) { @@ -248,14 +217,16 @@ var AttributeType = { * @param {number} offset The offset of the attribute data in the currently bound GL buffer. * @param {Array} arguments to be passed to disabled attribute value functions */ -Bucket.prototype.setAttribPointers = function(programName, gl, program, offset, args) { +Bucket.prototype.setAttribPointers = function(programName, gl, program, offset, layer, args) { var attribute; // Set disabled attributes var disabledAttributes = this.attributes[programName].disabled; for (var i = 0; i < disabledAttributes.length; i++) { attribute = disabledAttributes[i]; - var attributeId = program['a_' + attribute.name]; + if (attribute.isLayerConstant === false && layer.id !== attribute.layerId) continue; + + var attributeId = program[attribute.programName]; gl.disableVertexAttribArray(attributeId); gl['vertexAttrib' + attribute.components + 'fv'](attributeId, attribute.getValue.apply(this, args)); } @@ -266,6 +237,7 @@ Bucket.prototype.setAttribPointers = function(programName, gl, program, offset, for (var j = 0; j < enabledAttributes.length; j++) { attribute = enabledAttributes[j]; + if (attribute.isLayerConstant === false && layer.id !== attribute.layerId) continue; gl.vertexAttribPointer( program[attribute.programName], @@ -329,10 +301,7 @@ Bucket.prototype.getBufferName = function(programName, type) { Bucket.prototype.serialize = function() { return { - layer: { - id: this.layer.id, - type: this.layer.type - }, + layer: this.layer.serialize(), zoom: this.zoom, elementGroups: this.elementGroups, arrays: util.mapObject(this.arrays, function(array) { @@ -340,7 +309,7 @@ Bucket.prototype.serialize = function() { }), arrayTypes: this.arrayTypes, childLayers: this.childLayers.map(function(layer) { - return { id: layer.id }; + return layer.serialize(); }) }; }; @@ -374,19 +343,25 @@ Bucket.prototype._premultiplyColor = util.premultiply; var createVertexAddMethodCache = {}; -function createVertexAddMethod(programName, programInterface, bufferName, enabledAttributes) { - var body = ''; +function createVertexAddMethod(bucket, interfaceName) { + var enabledAttributes = bucket.attributes[interfaceName].enabled; + var programInterface = bucket.programInterfaces[interfaceName]; + + var body = 'var layer;\n'; var pushArgs = []; + for (var i = 0; i < enabledAttributes.length; i++) { var attribute = enabledAttributes[i]; var attributePushArgs = []; if (Array.isArray(attribute.value)) { - attributePushArgs = attribute.value; + attributePushArgs = attributePushArgs.concat(attribute.value); + attributePushArgs[0] = '(layer = this.childLayers[' + attribute.layerIndex + '] ,' + attributePushArgs[0] + ')'; } else { + body += 'layer = this.childLayers[' + attribute.layerIndex + '];\n'; var attributeId = '_' + i; - body += 'var ' + attributeId + ' = ' + attribute.value + ';'; + body += 'var ' + attributeId + ' = ' + attribute.value + ';\n'; for (var j = 0; j < attribute.components; j++) { attributePushArgs.push(attributeId + '[' + j + ']'); } @@ -405,6 +380,7 @@ function createVertexAddMethod(programName, programInterface, bufferName, enable pushArgs = pushArgs.concat(multipliedAttributePushArgs); } + var bufferName = bucket.getBufferName(interfaceName, 'vertex'); body += 'return this.arrays.' + bufferName + '.emplaceBack(' + pushArgs.join(',') + ');'; if (!createVertexAddMethodCache[body]) { @@ -431,24 +407,27 @@ function createElementBufferType(components) { } var _getAttributeValueCache = {}; -function createGetAttributeValueMethod(bucket, interfaceName, attribute) { +function createGetAttributeValueMethod(bucket, interfaceName, attribute, layerIndex) { if (!_getAttributeValueCache[interfaceName]) { _getAttributeValueCache[interfaceName] = {}; } if (!_getAttributeValueCache[interfaceName][attribute.name]) { var bodyArgs = bucket.programInterfaces[interfaceName].attributeArgs; - var body = 'return '; + var body = ''; + + body += 'var layer = this.childLayers[' + layerIndex + '];\n'; if (Array.isArray(attribute.value)) { - body += '[' + attribute.value.join(', ') + ']'; + body += 'return [' + attribute.value.join(', ') + ']'; } else { - body += attribute.value; + body += 'return ' + attribute.value; } if (attribute.multiplier) { body += '.map(function(v) { return v * ' + attribute.multiplier + '; })'; } + body += ';'; _getAttributeValueCache[interfaceName][attribute.name] = new Function(bodyArgs, body); } @@ -459,3 +438,46 @@ function createGetAttributeValueMethod(bucket, interfaceName, attribute) { function capitalize(string) { return string.charAt(0).toUpperCase() + string.slice(1); } + +function createAttributes(bucket) { + var attributes = {}; + for (var interfaceName in bucket.programInterfaces) { + var interfaceAttributes = attributes[interfaceName] = { enabled: [], disabled: [] }; + var interface_ = bucket.programInterfaces[interfaceName]; + for (var i = 0; i < interface_.attributes.length; i++) { + var attribute = interface_.attributes[i]; + for (var j = 0; j < bucket.childLayers.length; j++) { + var layer = bucket.childLayers[j]; + if (attribute.isLayerConstant !== false && layer.id !== bucket.layer.id) continue; + if (isAttributeDisabled(bucket, attribute, layer)) { + interfaceAttributes.disabled.push(util.extend({}, attribute, { + getValue: createGetAttributeValueMethod(bucket, interfaceName, attribute, j), + name: layer.id + '__' + attribute.name, + programName: 'a_' + attribute.name, + layerId: layer.id, + layerIndex: j + })); + } else { + interfaceAttributes.enabled.push(util.extend({}, attribute, { + name: layer.id + '__' + attribute.name, + programName: 'a_' + attribute.name, + layerId: layer.id, + layerIndex: j + })); + } + } + } + } + return attributes; +} + + +function isAttributeDisabled(bucket, attribute, layer) { + if (attribute.isDisabled === undefined || attribute.isDisabled === false) { + return false; + } else if (attribute.isDisabled === true) { + return true; + } else { + return !!attribute.isDisabled.call(bucket, layer); + } +} diff --git a/js/data/bucket/circle_bucket.js b/js/data/bucket/circle_bucket.js index 8ec0c78393b..1d293aa370d 100644 --- a/js/data/bucket/circle_bucket.js +++ b/js/data/bucket/circle_bucket.js @@ -39,17 +39,18 @@ CircleBucket.prototype.programInterfaces = { name: 'color', components: 4, type: 'Uint8', + isLayerConstant: false, value: ( 'this._premultiplyColor(' + - 'this.layer.getPaintValue("circle-color", globalProperties, featureProperties),' + - 'this.layer.getPaintValue("circle-opacity", globalProperties, featureProperties)' + + 'layer.getPaintValue("circle-color", globalProperties, featureProperties),' + + 'layer.getPaintValue("circle-opacity", globalProperties, featureProperties)' + ')' ), multiplier: 255, - isDisabled: function() { + isDisabled: function(layer) { return ( - this.layer.isPaintValueFeatureConstant("circle-color") && - this.layer.isPaintValueFeatureConstant('circle-opacity') + layer.isPaintValueFeatureConstant("circle-color") && + layer.isPaintValueFeatureConstant('circle-opacity') ); } }] diff --git a/js/render/draw_circle.js b/js/render/draw_circle.js index 553afa70a25..a538d6fc193 100644 --- a/js/render/draw_circle.js +++ b/js/render/draw_circle.js @@ -49,7 +49,7 @@ function drawCircles(painter, source, layer, coords) { for (var k = 0; k < elementGroups.length; k++) { var group = elementGroups[k]; var count = group.elementLength * 3; - bucket.setAttribPointers('circle', gl, program, group.vertexOffset, [{$zoom: painter.transform.zoom}]); + bucket.setAttribPointers('circle', gl, program, group.vertexOffset, layer, [{$zoom: painter.transform.zoom}]); gl.drawElements(gl.TRIANGLES, count, gl.UNSIGNED_SHORT, group.elementOffset); } } diff --git a/js/render/draw_collision_debug.js b/js/render/draw_collision_debug.js index 3737102249d..c6257f4563b 100644 --- a/js/render/draw_collision_debug.js +++ b/js/render/draw_collision_debug.js @@ -19,7 +19,7 @@ function drawCollisionDebug(painter, source, layer, coords) { if (elementGroups[0].vertexLength === 0) continue; bucket.bindBuffers('collisionBox', gl); - bucket.setAttribPointers('collisionBox', gl, program, elementGroups[0].vertexOffset); + bucket.setAttribPointers('collisionBox', gl, program, elementGroups[0].vertexOffset, layer); painter.setPosMatrix(coord.posMatrix); diff --git a/js/render/draw_fill.js b/js/render/draw_fill.js index b22084b0c71..ede52c72e28 100644 --- a/js/render/draw_fill.js +++ b/js/render/draw_fill.js @@ -116,7 +116,7 @@ function drawFill(painter, source, layer, coord) { for (var i = 0; i < elementGroups.length; i++) { var group = elementGroups[i]; - bucket.setAttribPointers('fill', gl, fillProgram, group.vertexOffset); + bucket.setAttribPointers('fill', gl, fillProgram, group.vertexOffset, layer); var count = group.elementLength * 3; gl.drawElements(gl.TRIANGLES, count, gl.UNSIGNED_SHORT, group.elementOffset); @@ -183,7 +183,7 @@ function drawStroke(painter, source, layer, coord) { for (var k = 0; k < elementGroups.length; k++) { var group = elementGroups[k]; - bucket.setAttribPointers('fill', gl, program, group.vertexOffset); + bucket.setAttribPointers('fill', gl, program, group.vertexOffset, layer); var count = group.secondElementLength * 2; gl.drawElements(gl.LINES, count, gl.UNSIGNED_SHORT, group.secondElementOffset); diff --git a/js/render/draw_line.js b/js/render/draw_line.js index 14ee9fc1573..0ff7e30a859 100644 --- a/js/render/draw_line.js +++ b/js/render/draw_line.js @@ -165,7 +165,7 @@ module.exports = function drawLine(painter, source, layer, coords) { for (var i = 0; i < elementGroups.length; i++) { var group = elementGroups[i]; - bucket.setAttribPointers('line', gl, program, group.vertexOffset); + bucket.setAttribPointers('line', gl, program, group.vertexOffset, layer); var count = group.elementLength * 3; gl.drawElements(gl.TRIANGLES, count, gl.UNSIGNED_SHORT, group.elementOffset); diff --git a/js/render/draw_symbol.js b/js/render/draw_symbol.js index 9b7f2fd7080..82b27154c8b 100644 --- a/js/render/draw_symbol.js +++ b/js/render/draw_symbol.js @@ -161,7 +161,8 @@ function drawSymbol(painter, layer, posMatrix, tile, bucket, elementGroups, pref for (var j = 0; j < elementGroups.length; j++) { group = elementGroups[j]; - bucket.setAttribPointers(programInterfaceName, gl, program, group.vertexOffset); + bucket.setAttribPointers(programInterfaceName, gl, program, group.vertexOffset, layer); + count = group.elementLength * 3; gl.drawElements(gl.TRIANGLES, count, gl.UNSIGNED_SHORT, group.elementOffset); } @@ -175,7 +176,7 @@ function drawSymbol(painter, layer, posMatrix, tile, bucket, elementGroups, pref for (var i = 0; i < elementGroups.length; i++) { group = elementGroups[i]; bucket.bindBuffers(programInterfaceName, gl); - bucket.setAttribPointers(programInterfaceName, gl, program, group.vertexOffset); + bucket.setAttribPointers(programInterfaceName, gl, program, group.vertexOffset, layer); count = group.elementLength * 3; gl.drawElements(gl.TRIANGLES, count, gl.UNSIGNED_SHORT, group.elementOffset); @@ -185,7 +186,7 @@ function drawSymbol(painter, layer, posMatrix, tile, bucket, elementGroups, pref gl.uniform1f(program.u_opacity, layer.paint['icon-opacity']); for (var k = 0; k < elementGroups.length; k++) { group = elementGroups[k]; - bucket.setAttribPointers(programInterfaceName, gl, program, group.vertexOffset); + bucket.setAttribPointers(programInterfaceName, gl, program, group.vertexOffset, layer); count = group.elementLength * 3; gl.drawElements(gl.TRIANGLES, count, gl.UNSIGNED_SHORT, group.elementOffset); diff --git a/test/js/data/bucket.test.js b/test/js/data/bucket.test.js index c463132ca06..3ad6f564145 100644 --- a/test/js/data/bucket.test.js +++ b/test/js/data/bucket.test.js @@ -57,8 +57,8 @@ test('Bucket', function(t) { function create() { var Class = createClass(); return new Class({ - layer: { type: 'circle' }, - childLayers: [{ type: 'circle' }], + layer: { id: 'layerid', type: 'circle' }, + childLayers: [{ id: 'layerid', type: 'circle' }], buffers: {} }); } @@ -72,9 +72,9 @@ test('Bucket', function(t) { var testVertex = bucket.arrays.testVertex; t.equal(testVertex.length, 1); var v0 = testVertex.get(0); - t.equal(v0.map, 17); - t.equal(v0.box0, 34); - t.equal(v0.box1, 84); + t.equal(v0.layerid__map, 17); + t.equal(v0.layerid__box0, 34); + t.equal(v0.layerid__box1, 84); var testElement = bucket.arrays.testElement; t.equal(testElement.length, 1); @@ -121,9 +121,9 @@ test('Bucket', function(t) { var testVertex = bucket.arrays.testVertex; t.equal(testVertex.length, 1); var v0 = testVertex.get(0); - t.equal(v0.map, 17); - t.equal(v0.box0, 34); - t.equal(v0.box1, 84); + t.equal(v0.layerid__map, 17); + t.equal(v0.layerid__box0, 34); + t.equal(v0.layerid__box1, 84); var testElement = bucket.arrays.testElement; t.equal(testElement.length, 1); From 48d7f132ecade64645aa5804905cb5ef24820909 Mon Sep 17 00:00:00 2001 From: Lucas Wojciechowski Date: Wed, 9 Mar 2016 11:42:32 -0800 Subject: [PATCH 13/32] Use a layers object instead of an array in WorkerTile --- js/source/worker.js | 15 +++----- js/source/worker_tile.js | 37 +++++++++++++------ test/js/source/worker_tile.test.js | 57 ++++++++++++++++++++---------- 3 files changed, 69 insertions(+), 40 deletions(-) diff --git a/js/source/worker.js b/js/source/worker.js index 63ec68f683c..f33e38764d5 100644 --- a/js/source/worker.js +++ b/js/source/worker.js @@ -23,24 +23,19 @@ function Worker(self) { this.loading = {}; this.loaded = {}; - this.layers = []; this.geoJSONIndexes = {}; } util.extend(Worker.prototype, { 'set layers': function(layers) { - this.layers = layers; + this.layers = {}; + for (var i = 0; i < layers.length; i++) { + this.layers[layers[i].id] = layers[i]; + } }, 'update layers': function(layers) { - var layersById = {}; - var i; - for (i = 0; i < layers.length; i++) { - layersById[layers[i].id] = layers[i]; - } - for (i = 0; i < this.layers.length; i++) { - this.layers[i] = layersById[this.layers[i].id] || this.layers[i]; - } + util.extend(this.layers, layers); }, 'load tile': function(params, callback) { diff --git a/js/source/worker_tile.js b/js/source/worker_tile.js index f05cbf55eed..91f3c50a522 100644 --- a/js/source/worker_tile.js +++ b/js/source/worker_tile.js @@ -40,17 +40,12 @@ WorkerTile.prototype.parse = function(data, layers, actor, rawTileData, callback var sourceLayerId; var bucket; - var childLayers = {}; - for (i = 0; i < layers.length; i++) { - layer = layers[i]; - var key = layer.ref || layer.id; - childLayers[key] = childLayers[key] || []; - childLayers[key].push(layer); - } + var layerFamilies = createLayerFamilies(this, layers); // Map non-ref layers to buckets. - for (i = 0; i < layers.length; i++) { - layer = layers[i]; + var bucketIndex = 0; + for (var parentName in layerFamilies) { + layer = layers[parentName]; if (layer.source !== this.source) continue; if (layer.ref) continue; @@ -61,8 +56,8 @@ WorkerTile.prototype.parse = function(data, layers, actor, rawTileData, callback bucket = Bucket.create({ layer: layer, - index: i, - childLayers: childLayers[layer.id], + index: bucketIndex++, + childLayers: layerFamilies[parentName], zoom: this.zoom, overscaling: this.overscaling, showCollisionBoxes: this.showCollisionBoxes, @@ -274,3 +269,23 @@ function getTransferables(buckets) { function getLayerId(layer) { return layer.id; } + +function createLayerFamilies(tile, layers) { + var families = {}; + + for (var childId in layers) { + var child = layers[childId]; + var parentId = child.ref || child.id; + var parent = layers[parentId]; + + if (parent.source !== tile.source) continue; + if (parent.minzoom && tile.zoom < parent.minzoom) continue; + if (parent.maxzoom && tile.zoom >= parent.maxzoom) continue; + if (parent.layout && parent.layout.visibility === 'none') continue; + + families[parentId] = families[parentId] || []; + families[parentId].push(child); + } + + return families; +} diff --git a/test/js/source/worker_tile.test.js b/test/js/source/worker_tile.test.js index a39351296e9..a85c3939271 100644 --- a/test/js/source/worker_tile.test.js +++ b/test/js/source/worker_tile.test.js @@ -6,25 +6,34 @@ var Wrapper = require('../../../js/source/geojson_wrapper'); var TileCoord = require('../../../js/source/tile_coord'); test('basic', function(t) { - var buckets = [{ - id: 'test', - source: 'source', - type: 'circle', - layout: {}, - compare: function () { return true; } - }]; - var features = [{ type: 1, geometry: [0, 0], tags: {} }]; - var tile = new WorkerTile({uid: '', zoom: 0, maxZoom: 20, tileSize: 512, source: 'source', - coord: new TileCoord(1, 1, 1), overscaling: 1 }); + var tile = new WorkerTile({ + uid: '', + zoom: 0, + maxZoom: 20, + tileSize: 512, + source: 'source', + coord: new TileCoord(1, 1, 1), + overscaling: 1 + }); t.test('basic worker tile', function(t) { - tile.parse(new Wrapper(features), buckets, {}, null, function(err, result) { + var layers = { + test: { + id: 'test', + source: 'source', + type: 'circle', + layout: {}, + compare: function () { return true; } + } + }; + + tile.parse(new Wrapper(features), layers, {}, null, function(err, result) { t.equal(err, null); t.ok(result.buckets[0]); t.end(); @@ -32,14 +41,24 @@ test('basic', function(t) { }); t.test('hidden layers', function(t) { - buckets.push({ - id: 'test-hidden', - source: 'source', - type: 'fill', - layout: { visibility: 'none' }, - compare: function () { return true; } - }); - tile.parse(new Wrapper(features), buckets, {}, null, function(err, result) { + var layers = { + 'test': { + id: 'test', + source: 'source', + type: 'circle', + layout: {}, + compare: function () { return true; } + }, + 'test-hidden': { + id: 'test-hidden', + source: 'source', + type: 'fill', + layout: { visibility: 'none' }, + compare: function () { return true; } + } + }; + + tile.parse(new Wrapper(features), layers, {}, null, function(err, result) { t.equal(err, null); t.equal(Object.keys(result.buckets[0].elementGroups).length, 1, 'element groups exclude hidden layer'); t.end(); From f2058f0f5d9bbad0a7968ddf38594cafbe46a5ee Mon Sep 17 00:00:00 2001 From: Lucas Wojciechowski Date: Wed, 30 Mar 2016 15:34:35 -0700 Subject: [PATCH 14/32] Make bucket-side style construction faster --- js/data/bucket.js | 34 +++------- js/data/bucket/symbol_bucket.js | 4 ++ js/render/draw_circle.js | 1 - js/source/geojson_source.js | 2 +- js/source/tile.js | 17 +++-- js/source/vector_tile_source.js | 2 +- js/source/worker.js | 54 ++++++++++++++- js/source/worker_tile.js | 6 +- js/style/style_layer.js | 102 +++++++++++++++-------------- test/js/data/bucket.test.js | 5 +- test/js/data/fill_bucket.test.js | 3 +- test/js/data/line_bucket.test.js | 3 +- test/js/data/symbol_bucket.test.js | 8 ++- test/js/source/worker_tile.test.js | 13 ++-- 14 files changed, 155 insertions(+), 99 deletions(-) diff --git a/js/data/bucket.js b/js/data/bucket.js index fe0f036d8fd..70432d91eb1 100644 --- a/js/data/bucket.js +++ b/js/data/bucket.js @@ -2,7 +2,6 @@ var featureFilter = require('feature-filter'); var Buffer = require('./buffer'); -var StyleLayer = require('../style/style_layer'); var util = require('../util/util'); var StructArrayType = require('../util/struct_array'); @@ -76,7 +75,6 @@ function Bucket(options) { this.minZoom = this.layer.minzoom; this.maxZoom = this.layer.maxzoom; - this.createStyleLayers(); this.attributes = createAttributes(this); if (options.elementGroups) { @@ -95,6 +93,7 @@ function Bucket(options) { */ Bucket.prototype.populateBuffers = function() { this.createArrays(); + this.recalculateStyleLayers(); for (var i = 0; i < this.features.length; i++) { this.addFeature(this.features[i]); @@ -301,38 +300,19 @@ Bucket.prototype.getBufferName = function(programName, type) { Bucket.prototype.serialize = function() { return { - layer: this.layer.serialize(), + layerId: this.layer.id, zoom: this.zoom, elementGroups: this.elementGroups, arrays: util.mapObject(this.arrays, function(array) { return array.serialize(); }), arrayTypes: this.arrayTypes, - childLayers: this.childLayers.map(function(layer) { - return layer.serialize(); + childLayerIds: this.childLayers.map(function(layer) { + return layer.id; }) }; }; -Bucket.prototype.createStyleLayers = function(style) { - var that = this; - var refLayer = this.layer = create(this.layer); - this.childLayers = this.childLayers.map(create); - - function create(layer) { - if (style) { - return style.getLayer(layer.id); - } else if (!(layer instanceof StyleLayer)) { - layer = StyleLayer.create(layer, refLayer); - layer.updatePaintTransitions({}, {transition: false}); - layer.recalculate(that.zoom, { lastIntegerZoom: Infinity, lastIntegerZoomTime: 0, lastZoom: 0 }); - return layer; - } else { - return layer; - } - } -}; - Bucket.prototype.createFilter = function() { if (!this.filter) { this.filter = featureFilter(this.layer.filter); @@ -341,6 +321,12 @@ Bucket.prototype.createFilter = function() { Bucket.prototype._premultiplyColor = util.premultiply; +var FAKE_ZOOM_HISTORY = { lastIntegerZoom: Infinity, lastIntegerZoomTime: 0, lastZoom: 0 }; +Bucket.prototype.recalculateStyleLayers = function() { + for (var i = 0; i < this.childLayers.length; i++) { + this.childLayers[i].recalculate(this.zoom, FAKE_ZOOM_HISTORY); + } +}; var createVertexAddMethodCache = {}; function createVertexAddMethod(bucket, interfaceName) { diff --git a/js/data/bucket/symbol_bucket.js b/js/data/bucket/symbol_bucket.js index 443baf0ca5c..0ed102165cf 100644 --- a/js/data/bucket/symbol_bucket.js +++ b/js/data/bucket/symbol_bucket.js @@ -331,6 +331,8 @@ SymbolBucket.prototype.anchorIsTooClose = function(text, repeatDistance, anchor) }; SymbolBucket.prototype.placeFeatures = function(collisionTile, showCollisionBoxes) { + this.recalculateStyleLayers(); + // Calculate which labels can be shown and when they can be shown and // create the bufers used for rendering. @@ -478,6 +480,7 @@ SymbolBucket.prototype.addSymbols = function(programName, quads, scale, keepUpri }; SymbolBucket.prototype.updateIcons = function(icons) { + this.recalculateStyleLayers(); var iconValue = this.layer.layout['icon-image']; if (!iconValue) return; @@ -489,6 +492,7 @@ SymbolBucket.prototype.updateIcons = function(icons) { }; SymbolBucket.prototype.updateFont = function(stacks) { + this.recalculateStyleLayers(); var fontName = this.layer.layout['text-font'], stack = stacks[fontName] = stacks[fontName] || {}; diff --git a/js/render/draw_circle.js b/js/render/draw_circle.js index a538d6fc193..24d75943acb 100644 --- a/js/render/draw_circle.js +++ b/js/render/draw_circle.js @@ -33,7 +33,6 @@ function drawCircles(painter, source, layer, coords) { var tile = source.getTile(coord); var bucket = tile.getBucket(layer); if (!bucket) continue; - bucket.createStyleLayers(painter.style); var elementGroups = bucket.elementGroups.circle; if (!elementGroups) continue; diff --git a/js/source/geojson_source.js b/js/source/geojson_source.js index 2a075ae9042..3fca1636ae9 100644 --- a/js/source/geojson_source.js +++ b/js/source/geojson_source.js @@ -195,7 +195,7 @@ GeoJSONSource.prototype = util.inherit(Evented, /** @lends GeoJSONSource.prototy return; } - tile.loadVectorData(data); + tile.loadVectorData(data, this.map.style); if (tile.redoWhenDone) { tile.redoWhenDone = false; diff --git a/js/source/tile.js b/js/source/tile.js index eb3d3c65612..ef27296aada 100644 --- a/js/source/tile.js +++ b/js/source/tile.js @@ -42,7 +42,7 @@ Tile.prototype = { * @returns {undefined} * @private */ - loadVectorData: function(data) { + loadVectorData: function(data, style) { this.loaded = true; // empty GeoJSON tile @@ -52,7 +52,7 @@ Tile.prototype = { this.collisionTile = new CollisionTile(data.collisionTile, this.collisionBoxArray); this.featureIndex = new FeatureIndex(data.featureIndex, data.rawTileData, this.collisionTile); this.rawTileData = data.rawTileData; - this.buckets = unserializeBuckets(data.buckets); + this.buckets = unserializeBuckets(data.buckets, style); }, /** @@ -63,7 +63,7 @@ Tile.prototype = { * @returns {undefined} * @private */ - reloadSymbolData: function(data, painter) { + reloadSymbolData: function(data, painter, style) { if (this.isUnloaded) return; this.collisionTile = new CollisionTile(data.collisionTile, this.collisionBoxArray); @@ -79,7 +79,7 @@ Tile.prototype = { } // Add new symbol buckets - util.extend(this.buckets, unserializeBuckets(data.buckets)); + util.extend(this.buckets, unserializeBuckets(data.buckets, style)); }, /** @@ -122,7 +122,7 @@ Tile.prototype = { }, done.bind(this), this.workerID); function done(_, data) { - this.reloadSymbolData(data, source.map.painter); + this.reloadSymbolData(data, source.map.painter, source.map.style); source.fire('tile.load', {tile: this}); this.redoingPlacement = false; @@ -162,10 +162,13 @@ Tile.prototype = { } }; -function unserializeBuckets(input) { +function unserializeBuckets(input, style) { var output = {}; for (var i = 0; i < input.length; i++) { - var bucket = Bucket.create(input[i]); + var bucket = Bucket.create(util.extend({ + childLayers: input[i].childLayerIds.map(style.getLayer.bind(style)), + layer: style.getLayer(input[i].layerId) + }, input[i])); output[bucket.id] = bucket; } return output; diff --git a/js/source/vector_tile_source.js b/js/source/vector_tile_source.js index c5629f2f8be..ac67e90625f 100644 --- a/js/source/vector_tile_source.js +++ b/js/source/vector_tile_source.js @@ -89,7 +89,7 @@ VectorTileSource.prototype = util.inherit(Evented, { return; } - tile.loadVectorData(data); + tile.loadVectorData(data, this.map.style); if (tile.redoWhenDone) { tile.redoWhenDone = false; diff --git a/js/source/worker.js b/js/source/worker.js index f33e38764d5..40cd3b5e8bc 100644 --- a/js/source/worker.js +++ b/js/source/worker.js @@ -2,6 +2,7 @@ var Actor = require('../util/actor'); var WorkerTile = require('./worker_tile'); +var StyleLayer = require('../style/style_layer'); var util = require('../util/util'); var ajax = require('../util/ajax'); var vt = require('vector-tile'); @@ -29,13 +30,62 @@ function Worker(self) { util.extend(Worker.prototype, { 'set layers': function(layers) { this.layers = {}; + var that = this; + + // Filter layers and create an id -> layer map + var childLayerIndicies = []; for (var i = 0; i < layers.length; i++) { - this.layers[layers[i].id] = layers[i]; + var layer = layers[i]; + if (layer.type === 'fill' || layer.type === 'line' || layer.type === 'circle' || layer.type === 'symbol') { + if (layer.ref) { + childLayerIndicies.push(i); + } else { + setLayer(layer); + } + } + } + + // Create an instance of StyleLayer per layer + for (var j = 0; j < childLayerIndicies.length; j++) { + setLayer(layers[childLayerIndicies[j]]); } + + function setLayer(serializedLayer) { + var styleLayer = StyleLayer.create( + serializedLayer, + serializedLayer.ref && that.layers[serializedLayer.ref] + ); + styleLayer.updatePaintTransitions({}, {transition: false}); + that.layers[styleLayer.id] = styleLayer; + } + }, 'update layers': function(layers) { - util.extend(this.layers, layers); + var that = this; + var id; + var layer; + + // Update ref parents + for (id in layers) { + layer = layers[id]; + if (layer.ref) updateLayer(layer); + } + + // Update ref children + for (id in layers) { + layer = layers[id]; + if (!layer.ref) updateLayer(layer); + } + + function updateLayer(layer) { + if (that.layers[layer.id]) { + that.layers[layer.id].set(layer); + } else { + that.layers[layer.id] = StyleLayer.create(layer, that.layers[layer.ref]); + } + that.layers[layer.id].updatePaintTransitions({}, {transition: false}); + } }, 'load tile': function(params, callback) { diff --git a/js/source/worker_tile.js b/js/source/worker_tile.js index 91f3c50a522..206aacdb918 100644 --- a/js/source/worker_tile.js +++ b/js/source/worker_tile.js @@ -52,7 +52,7 @@ WorkerTile.prototype.parse = function(data, layers, actor, rawTileData, callback if (layer.minzoom && this.zoom < layer.minzoom) continue; if (layer.maxzoom && this.zoom >= layer.maxzoom) continue; if (layer.layout && layer.layout.visibility === 'none') continue; - if (data.layers && !data.layers[layer['source-layer']]) continue; + if (data.layers && !data.layers[layer.sourceLayer]) continue; bucket = Bucket.create({ layer: layer, @@ -62,14 +62,14 @@ WorkerTile.prototype.parse = function(data, layers, actor, rawTileData, callback overscaling: this.overscaling, showCollisionBoxes: this.showCollisionBoxes, collisionBoxArray: this.collisionBoxArray, - sourceLayerIndex: sourceLayerCoder.encode(layer['source-layer'] || '_geojsonTileLayer') + sourceLayerIndex: sourceLayerCoder.encode(layer.sourceLayer || '_geojsonTileLayer') }); bucket.createFilter(); bucketsById[layer.id] = bucket; if (data.layers) { // vectortile - sourceLayerId = layer['source-layer']; + sourceLayerId = layer.sourceLayer; bucketsBySourceLayer[sourceLayerId] = bucketsBySourceLayer[sourceLayerId] || {}; bucketsBySourceLayer[sourceLayerId][layer.id] = bucket; } diff --git a/js/style/style_layer.js b/js/style/style_layer.js index 1020a8fd21f..098979fe578 100644 --- a/js/style/style_layer.js +++ b/js/style/style_layer.js @@ -25,60 +25,64 @@ StyleLayer.create = function(layer, refLayer) { }; function StyleLayer(layer, refLayer) { - this.id = layer.id; - this.ref = layer.ref; - this.metadata = layer.metadata; - this.type = (refLayer || layer).type; - this.source = (refLayer || layer).source; - this.sourceLayer = (refLayer || layer)['source-layer']; - this.minzoom = (refLayer || layer).minzoom; - this.maxzoom = (refLayer || layer).maxzoom; - this.filter = (refLayer || layer).filter; - - this.paint = {}; - this.layout = {}; - - this._paintSpecifications = styleSpec['paint_' + this.type]; - this._layoutSpecifications = styleSpec['layout_' + this.type]; - - this._paintTransitions = {}; // {[propertyName]: StyleTransition} - this._paintTransitionOptions = {}; // {[className]: {[propertyName]: { duration:Number, delay:Number }}} - this._paintDeclarations = {}; // {[className]: {[propertyName]: StyleDeclaration}} - this._layoutDeclarations = {}; // {[propertyName]: StyleDeclaration} - this._layoutFunctions = {}; // {[propertyName]: Boolean} - - var paintName, layoutName; - - // Resolve paint declarations - for (var key in layer) { - var match = key.match(/^paint(?:\.(.*))?$/); - if (match) { - var klass = match[1] || ''; - for (paintName in layer[key]) { - this.setPaintProperty(paintName, layer[key][paintName], klass); + this.set(layer, refLayer); +} + +StyleLayer.prototype = util.inherit(Evented, { + + set: function(layer, refLayer) { + this.id = layer.id; + this.ref = layer.ref; + this.metadata = layer.metadata; + this.type = (refLayer || layer).type; + this.source = (refLayer || layer).source; + this.sourceLayer = (refLayer || layer)['source-layer']; + this.minzoom = (refLayer || layer).minzoom; + this.maxzoom = (refLayer || layer).maxzoom; + this.filter = (refLayer || layer).filter; + + this.paint = {}; + this.layout = {}; + + this._paintSpecifications = styleSpec['paint_' + this.type]; + this._layoutSpecifications = styleSpec['layout_' + this.type]; + + this._paintTransitions = {}; // {[propertyName]: StyleTransition} + this._paintTransitionOptions = {}; // {[className]: {[propertyName]: { duration:Number, delay:Number }}} + this._paintDeclarations = {}; // {[className]: {[propertyName]: StyleDeclaration}} + this._layoutDeclarations = {}; // {[propertyName]: StyleDeclaration} + this._layoutFunctions = {}; // {[propertyName]: Boolean} + + var paintName, layoutName; + + // Resolve paint declarations + for (var key in layer) { + var match = key.match(/^paint(?:\.(.*))?$/); + if (match) { + var klass = match[1] || ''; + for (paintName in layer[key]) { + this.setPaintProperty(paintName, layer[key][paintName], klass); + } } } - } - // Resolve layout declarations - if (this.ref) { - this._layoutDeclarations = refLayer._layoutDeclarations; - } else { - for (layoutName in layer.layout) { - this.setLayoutProperty(layoutName, layer.layout[layoutName]); + // Resolve layout declarations + if (this.ref) { + this._layoutDeclarations = refLayer._layoutDeclarations; + } else { + for (layoutName in layer.layout) { + this.setLayoutProperty(layoutName, layer.layout[layoutName]); + } } - } - // set initial layout/paint values - for (paintName in this._paintSpecifications) { - this.paint[paintName] = this.getPaintValue(paintName); - } - for (layoutName in this._layoutSpecifications) { - this._updateLayoutValue(layoutName); - } -} - -StyleLayer.prototype = util.inherit(Evented, { + // set initial layout/paint values + for (paintName in this._paintSpecifications) { + this.paint[paintName] = this.getPaintValue(paintName); + } + for (layoutName in this._layoutSpecifications) { + this._updateLayoutValue(layoutName); + } + }, setLayoutProperty: function(name, value) { diff --git a/test/js/data/bucket.test.js b/test/js/data/bucket.test.js index 3ad6f564145..d5d2395ab87 100644 --- a/test/js/data/bucket.test.js +++ b/test/js/data/bucket.test.js @@ -3,6 +3,7 @@ var test = require('tap').test; var Bucket = require('../../../js/data/bucket'); var util = require('../../../js/util/util'); +var StyleLayer = require('../../../js/style/style_layer'); test('Bucket', function(t) { @@ -57,8 +58,8 @@ test('Bucket', function(t) { function create() { var Class = createClass(); return new Class({ - layer: { id: 'layerid', type: 'circle' }, - childLayers: [{ id: 'layerid', type: 'circle' }], + layer: new StyleLayer({ id: 'layerid', type: 'circle' }), + childLayers: [new StyleLayer({ id: 'layerid', type: 'circle' })], buffers: {} }); } diff --git a/test/js/data/fill_bucket.test.js b/test/js/data/fill_bucket.test.js index e4a2e322875..04a12fc3ca5 100644 --- a/test/js/data/fill_bucket.test.js +++ b/test/js/data/fill_bucket.test.js @@ -7,6 +7,7 @@ var VectorTile = require('vector-tile').VectorTile; var Point = require('point-geometry'); var FillBucket = require('../../../js/data/bucket/fill_bucket'); var path = require('path'); +var StyleLayer = require('../../../js/style/style_layer'); // Load a fill feature from fixture tile. var vt = new VectorTile(new Protobuf(new Uint8Array(fs.readFileSync(path.join(__dirname, '/../../fixtures/mbsv5-6-18-23.vector.pbf'))))); @@ -17,7 +18,7 @@ test('FillBucket', function(t) { var warn = console.warn; console.warn = function() {}; - var layer = { id: 'test', type: 'fill', layout: {} }; + var layer = new StyleLayer({ id: 'test', type: 'fill', layout: {} }); var bucket = new FillBucket({ buffers: {}, layer: layer, diff --git a/test/js/data/line_bucket.test.js b/test/js/data/line_bucket.test.js index a4110f82e0b..6621a018243 100644 --- a/test/js/data/line_bucket.test.js +++ b/test/js/data/line_bucket.test.js @@ -7,13 +7,14 @@ var Protobuf = require('pbf'); var VectorTile = require('vector-tile').VectorTile; var Point = require('point-geometry'); var LineBucket = require('../../../js/data/bucket/line_bucket'); +var StyleLayer = require('../../../js/style/style_layer'); // Load a line feature from fixture tile. var vt = new VectorTile(new Protobuf(new Uint8Array(fs.readFileSync(path.join(__dirname, '/../../fixtures/mbsv5-6-18-23.vector.pbf'))))); var feature = vt.layers.road.feature(0); test('LineBucket', function(t) { - var layer = { id: 'test', type: 'line', layout: {} }; + var layer = new StyleLayer({ id: 'test', type: 'line', layout: {} }); var bucket = new LineBucket({ buffers: {}, layer: layer, diff --git a/test/js/data/symbol_bucket.test.js b/test/js/data/symbol_bucket.test.js index 4f67dbde099..6eec9d7b48c 100644 --- a/test/js/data/symbol_bucket.test.js +++ b/test/js/data/symbol_bucket.test.js @@ -9,6 +9,7 @@ var SymbolBucket = require('../../../js/data/bucket/symbol_bucket'); var Collision = require('../../../js/symbol/collision_tile'); var CollisionBoxArray = require('../../../js/symbol/collision_box'); var GlyphAtlas = require('../../../js/symbol/glyph_atlas'); +var StyleLayer = require('../../../js/style/style_layer'); // Load a point feature from fixture tile. var vt = new VectorTile(new Protobuf(new Uint8Array(fs.readFileSync(path.join(__dirname, '/../../fixtures/mbsv5-6-18-23.vector.pbf'))))); @@ -29,7 +30,12 @@ test('SymbolBucket', function(t) { var stacks = { 'Test': glyphs }; function bucketSetup() { - var layer = { id: 'test', type: 'symbol', layout: {'text-font': ['Test'] }}; + var layer = new StyleLayer({ + id: 'test', + type: 'symbol', + layout: { 'text-font': ['Test'] } + }); + var bucket = new SymbolBucket({ buffers: buffers, overscaling: 1, diff --git a/test/js/source/worker_tile.test.js b/test/js/source/worker_tile.test.js index a85c3939271..353347e74ed 100644 --- a/test/js/source/worker_tile.test.js +++ b/test/js/source/worker_tile.test.js @@ -4,6 +4,7 @@ var test = require('tap').test; var WorkerTile = require('../../../js/source/worker_tile'); var Wrapper = require('../../../js/source/geojson_wrapper'); var TileCoord = require('../../../js/source/tile_coord'); +var StyleLayer = require('../../../js/style/style_layer'); test('basic', function(t) { var features = [{ @@ -24,13 +25,13 @@ test('basic', function(t) { t.test('basic worker tile', function(t) { var layers = { - test: { + test: new StyleLayer({ id: 'test', source: 'source', type: 'circle', layout: {}, compare: function () { return true; } - } + }) }; tile.parse(new Wrapper(features), layers, {}, null, function(err, result) { @@ -42,20 +43,20 @@ test('basic', function(t) { t.test('hidden layers', function(t) { var layers = { - 'test': { + 'test': new StyleLayer({ id: 'test', source: 'source', type: 'circle', layout: {}, compare: function () { return true; } - }, - 'test-hidden': { + }), + 'test-hidden': new StyleLayer({ id: 'test-hidden', source: 'source', type: 'fill', layout: { visibility: 'none' }, compare: function () { return true; } - } + }) }; tile.parse(new Wrapper(features), layers, {}, null, function(err, result) { From 4eaacdf81cb7537a7c62b16f3684654af5d9be61 Mon Sep 17 00:00:00 2001 From: Lucas Wojciechowski Date: Wed, 30 Mar 2016 15:54:38 -0700 Subject: [PATCH 15/32] Remove posMatrix and exMatrix arguments from Painter#useProgram --- js/render/draw_debug.js | 3 ++- js/render/draw_fill.js | 9 ++++++--- js/render/draw_raster.js | 3 ++- js/render/draw_symbol.js | 4 +++- js/render/painter.js | 6 ++++-- js/render/painter/use_program.js | 5 +---- 6 files changed, 18 insertions(+), 12 deletions(-) diff --git a/js/render/draw_debug.js b/js/render/draw_debug.js index 806e51b30b0..16a86aaa8d9 100644 --- a/js/render/draw_debug.js +++ b/js/render/draw_debug.js @@ -23,7 +23,8 @@ function drawDebugTile(painter, source, coord) { painter.lineWidth(1 * browser.devicePixelRatio); var posMatrix = coord.posMatrix; - var program = painter.useProgram('debug', posMatrix); + var program = painter.useProgram('debug'); + painter.setPosMatrix(posMatrix); // draw bounding rectangle gl.bindBuffer(gl.ARRAY_BUFFER, painter.debugBuffer); diff --git a/js/render/draw_fill.js b/js/render/draw_fill.js index ede52c72e28..de108fdcb15 100644 --- a/js/render/draw_fill.js +++ b/js/render/draw_fill.js @@ -110,7 +110,8 @@ function drawFill(painter, source, layer, coord) { painter.depthMask(false); // Draw the actual triangle fan into the stencil buffer. - var fillProgram = painter.useProgram('fill', translatedPosMatrix); + var fillProgram = painter.useProgram('fill'); + painter.setPosMatrix(translatedPosMatrix); bucket.bindBuffers('fill', gl); @@ -134,7 +135,7 @@ function drawFill(painter, source, layer, coord) { if (image) { // Draw texture fill - program = painter.useProgram('pattern', posMatrix); + program = painter.useProgram('pattern'); setPattern(image, opacity, tile, coord, painter, program); gl.activeTexture(gl.TEXTURE0); @@ -142,10 +143,12 @@ function drawFill(painter, source, layer, coord) { } else { // Draw filling rectangle. - program = painter.useProgram('fill', posMatrix); + program = painter.useProgram('fill'); gl.uniform4fv(fillProgram.u_color, color); } + painter.setPosMatrix(posMatrix); + // Only draw regions that we marked gl.stencilFunc(gl.NOTEQUAL, 0x0, 0x07); gl.bindBuffer(gl.ARRAY_BUFFER, painter.tileExtentBuffer); diff --git a/js/render/draw_raster.js b/js/render/draw_raster.js index 1be55317c56..0e08359f9ec 100644 --- a/js/render/draw_raster.js +++ b/js/render/draw_raster.js @@ -36,7 +36,8 @@ function drawRasterTile(painter, source, layer, coord) { var tile = source.getTile(coord); var posMatrix = painter.transform.calculatePosMatrix(coord, source.maxzoom); - var program = painter.useProgram('raster', posMatrix); + var program = painter.useProgram('raster'); + painter.setPosMatrix(posMatrix); // color parameters gl.uniform1f(program.u_brightness_low, layer.paint['raster-brightness-min']); diff --git a/js/render/draw_symbol.js b/js/render/draw_symbol.js index 82b27154c8b..94cb54cbc26 100644 --- a/js/render/draw_symbol.js +++ b/js/render/draw_symbol.js @@ -106,7 +106,9 @@ function drawSymbol(painter, layer, posMatrix, tile, bucket, elementGroups, pref gl.activeTexture(gl.TEXTURE0); - var program = painter.useProgram(sdf ? 'sdf' : 'icon', posMatrix, exMatrix); + var program = painter.useProgram(sdf ? 'sdf' : 'icon'); + painter.setPosMatrix(posMatrix); + painter.setExMatrix(exMatrix); var texsize; if (text) { diff --git a/js/render/painter.js b/js/render/painter.js index 284a998a784..18d04fb0637 100644 --- a/js/render/painter.js +++ b/js/render/painter.js @@ -170,7 +170,8 @@ Painter.prototype._renderTileClippingMasks = function(coords) { gl.stencilFunc(gl.ALWAYS, id, 0xF8); - var program = this.useProgram('fill', coord.posMatrix); + var program = this.useProgram('fill'); + this.setPosMatrix(coord.posMatrix); // Draw the clipping mask gl.bindBuffer(gl.ARRAY_BUFFER, this.tileExtentBuffer); @@ -289,7 +290,8 @@ Painter.prototype.renderLayer = function(painter, source, layer, coords) { // Draws non-opaque areas. This is for debugging purposes. Painter.prototype.drawStencilBuffer = function() { var gl = this.gl; - var program = this.useProgram('fill', this.identityMatrix); + var program = this.useProgram('fill'); + this.setPosMatrix(this.identityMatrix); gl.stencilMask(0x00); gl.stencilFunc(gl.EQUAL, 0x80, 0x80); diff --git a/js/render/painter/use_program.js b/js/render/painter/use_program.js index cca0089453f..2df1f9a81e5 100644 --- a/js/render/painter/use_program.js +++ b/js/render/painter/use_program.js @@ -111,7 +111,7 @@ module.exports._createProgramCached = function(name) { return this.cache[name]; }; -module.exports.useProgram = function (nextProgramName, posMatrix, exMatrix) { +module.exports.useProgram = function (nextProgramName) { var gl = this.gl; var nextProgram = this._createProgramCached(nextProgramName); @@ -138,8 +138,5 @@ module.exports.useProgram = function (nextProgramName, posMatrix, exMatrix) { this.currentProgram = nextProgram; } - if (posMatrix !== undefined) this.setPosMatrix(posMatrix); - if (exMatrix !== undefined) this.setExMatrix(exMatrix); - return nextProgram; }; From 73e5320bc2e5fb9cb2e419cfc79ca3d65b85c720 Mon Sep 17 00:00:00 2001 From: Lucas Wojciechowski Date: Wed, 30 Mar 2016 16:11:19 -0700 Subject: [PATCH 16/32] Allow token replacement in shaders --- js/render/draw_circle.js | 4 +++- js/render/painter/use_program.js | 28 +++++++++++++++++++--------- shaders/circle.vertex.glsl | 3 ++- 3 files changed, 24 insertions(+), 11 deletions(-) diff --git a/js/render/draw_circle.js b/js/render/draw_circle.js index 24d75943acb..6c5cb628bba 100644 --- a/js/render/draw_circle.js +++ b/js/render/draw_circle.js @@ -9,7 +9,9 @@ function drawCircles(painter, source, layer, coords) { var gl = painter.gl; - var program = painter.useProgram('circle'); + var program = painter.useProgram('circle', { + colorType: 'attribute' + }); painter.setDepthSublayer(0); painter.depthMask(false); diff --git a/js/render/painter/use_program.js b/js/render/painter/use_program.js index 2df1f9a81e5..a72f729295c 100644 --- a/js/render/painter/use_program.js +++ b/js/render/painter/use_program.js @@ -61,19 +61,19 @@ var definitions = { } }; -module.exports._createProgram = function(name) { +module.exports._createProgram = function(name, tokens) { var gl = this.gl; var program = gl.createProgram(); var definition = definitions[name]; var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER); - gl.shaderSource(fragmentShader, definition.fragmentSource); + gl.shaderSource(fragmentShader, applyTokens(definition.fragmentSource, tokens)); gl.compileShader(fragmentShader); assert(gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS), gl.getShaderInfoLog(fragmentShader)); gl.attachShader(program, fragmentShader); var vertexShader = gl.createShader(gl.VERTEX_SHADER); - gl.shaderSource(vertexShader, definition.vertexSource); + gl.shaderSource(vertexShader, applyTokens(definition.vertexSource, tokens)); gl.compileShader(vertexShader); assert(gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS), gl.getShaderInfoLog(vertexShader)); gl.attachShader(program, vertexShader); @@ -103,18 +103,19 @@ module.exports._createProgram = function(name) { }, attributes, uniforms); }; -module.exports._createProgramCached = function(name) { +module.exports._createProgramCached = function(name, tokens) { this.cache = this.cache || {}; - if (!this.cache[name]) { - this.cache[name] = this._createProgram(name); + var key = JSON.stringify({name: name, tokens: tokens}); + if (!this.cache[key]) { + this.cache[key] = this._createProgram(name, tokens); } - return this.cache[name]; + return this.cache[key]; }; -module.exports.useProgram = function (nextProgramName) { +module.exports.useProgram = function (nextProgramName, tokens) { var gl = this.gl; - var nextProgram = this._createProgramCached(nextProgramName); + var nextProgram = this._createProgramCached(nextProgramName, tokens); var previousProgram = this.currentProgram; if (previousProgram !== nextProgram) { @@ -140,3 +141,12 @@ module.exports.useProgram = function (nextProgramName) { return nextProgram; }; + +function applyTokens(string, tokens) { + tokens = tokens || {}; + for (var key in tokens) { + var value = tokens[key]; + string = string.replace('{{' + key + '}}', value); + } + return string; +} diff --git a/shaders/circle.vertex.glsl b/shaders/circle.vertex.glsl index 8ae53c97de3..d47bb493d62 100644 --- a/shaders/circle.vertex.glsl +++ b/shaders/circle.vertex.glsl @@ -5,7 +5,8 @@ uniform mat4 u_exmatrix; uniform mediump float u_size; attribute vec2 a_pos; -attribute lowp vec4 a_color; + +{{colorType}} lowp vec4 a_color; varying vec2 v_extrude; varying lowp vec4 v_color; From db2be07756e7010b8a3ee07451ccbbcd7e76d214 Mon Sep 17 00:00:00 2001 From: Lucas Wojciechowski Date: Mon, 4 Apr 2016 12:07:58 -0700 Subject: [PATCH 17/32] Use shader source rewriting to disable attributes fixes #1714 --- js/data/bucket.js | 29 +++++++++++++++++++++++++---- js/render/draw_circle.js | 14 +++++--------- 2 files changed, 30 insertions(+), 13 deletions(-) diff --git a/js/data/bucket.js b/js/data/bucket.js index 70432d91eb1..6a657b5f3f8 100644 --- a/js/data/bucket.js +++ b/js/data/bucket.js @@ -226,8 +226,7 @@ Bucket.prototype.setAttribPointers = function(programName, gl, program, offset, if (attribute.isLayerConstant === false && layer.id !== attribute.layerId) continue; var attributeId = program[attribute.programName]; - gl.disableVertexAttribArray(attributeId); - gl['vertexAttrib' + attribute.components + 'fv'](attributeId, attribute.getValue.apply(this, args)); + gl['uniform' + attribute.components + 'fv'](attributeId, attribute.getValue.apply(this, args)); } // Set enabled attributes @@ -328,6 +327,26 @@ Bucket.prototype.recalculateStyleLayers = function() { } }; +Bucket.prototype.getUseProgramTokens = function(programInterface, layer) { + var tokens = {}; + + var enabledAttributes = this.attributes[programInterface].enabled; + for (var i = 0; i < enabledAttributes.length; i++) { + var enabledAttribute = enabledAttributes[i]; + if (enabledAttribute.isLayerConstant !== false && enabledAttribute.layerId !== layer.id) continue; + tokens[enabledAttribute.typeTokenName] = 'attribute'; + } + + var disabledAttributes = this.attributes[programInterface].disabled; + for (var j = 0; j < this.attributes[programInterface].disabled.length; j++) { + var disabledAttribute = disabledAttributes[j]; + if (disabledAttribute.isLayerConstant !== false && disabledAttribute.layerId !== layer.id) continue; + tokens[disabledAttribute.typeTokenName] = 'uniform'; + } + + return tokens; +}; + var createVertexAddMethodCache = {}; function createVertexAddMethod(bucket, interfaceName) { var enabledAttributes = bucket.attributes[interfaceName].enabled; @@ -441,14 +460,16 @@ function createAttributes(bucket) { name: layer.id + '__' + attribute.name, programName: 'a_' + attribute.name, layerId: layer.id, - layerIndex: j + layerIndex: j, + typeTokenName: attribute.name + 'Type' })); } else { interfaceAttributes.enabled.push(util.extend({}, attribute, { name: layer.id + '__' + attribute.name, programName: 'a_' + attribute.name, layerId: layer.id, - layerIndex: j + layerIndex: j, + typeTokenName: attribute.name + 'Type' })); } } diff --git a/js/render/draw_circle.js b/js/render/draw_circle.js index 6c5cb628bba..fda2a1e220c 100644 --- a/js/render/draw_circle.js +++ b/js/render/draw_circle.js @@ -9,10 +9,6 @@ function drawCircles(painter, source, layer, coords) { var gl = painter.gl; - var program = painter.useProgram('circle', { - colorType: 'attribute' - }); - painter.setDepthSublayer(0); painter.depthMask(false); @@ -26,9 +22,6 @@ function drawCircles(painter, source, layer, coords) { // are inversely related. var antialias = 1 / browser.devicePixelRatio / layer.paint['circle-radius']; - gl.uniform1f(program.u_blur, Math.max(layer.paint['circle-blur'], antialias)); - gl.uniform1f(program.u_size, layer.paint['circle-radius']); - for (var i = 0; i < coords.length; i++) { var coord = coords[i]; @@ -38,6 +31,11 @@ function drawCircles(painter, source, layer, coords) { var elementGroups = bucket.elementGroups.circle; if (!elementGroups) continue; + var program = painter.useProgram('circle', bucket.getUseProgramTokens('circle', layer)); + + gl.uniform1f(program.u_blur, Math.max(layer.paint['circle-blur'], antialias)); + gl.uniform1f(program.u_size, layer.paint['circle-radius']); + painter.setPosMatrix(painter.translatePosMatrix( coord.posMatrix, tile, @@ -54,6 +52,4 @@ function drawCircles(painter, source, layer, coords) { gl.drawElements(gl.TRIANGLES, count, gl.UNSIGNED_SHORT, group.elementOffset); } } - - gl.enableVertexAttribArray(program.a_color); } From dced2544d839329057d8d7b1cb92ca36eb336733 Mon Sep 17 00:00:00 2001 From: Lucas Wojciechowski Date: Tue, 5 Apr 2016 15:20:15 -0700 Subject: [PATCH 18/32] Update buffers when data-driven paint property changes --- js/data/bucket.js | 20 +++++++++++++------- js/source/worker.js | 5 +++-- js/style/style.js | 11 +++++++++++ 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/js/data/bucket.js b/js/data/bucket.js index 6a657b5f3f8..a55b211439a 100644 --- a/js/data/bucket.js +++ b/js/data/bucket.js @@ -236,6 +236,7 @@ Bucket.prototype.setAttribPointers = function(programName, gl, program, offset, for (var j = 0; j < enabledAttributes.length; j++) { attribute = enabledAttributes[j]; if (attribute.isLayerConstant === false && layer.id !== attribute.layerId) continue; + if (!getMember(attribute.name)) continue; gl.vertexAttribPointer( program[attribute.programName], @@ -333,15 +334,17 @@ Bucket.prototype.getUseProgramTokens = function(programInterface, layer) { var enabledAttributes = this.attributes[programInterface].enabled; for (var i = 0; i < enabledAttributes.length; i++) { var enabledAttribute = enabledAttributes[i]; - if (enabledAttribute.isLayerConstant !== false && enabledAttribute.layerId !== layer.id) continue; - tokens[enabledAttribute.typeTokenName] = 'attribute'; + if (enabledAttribute.isLayerConstant || enabledAttribute.layerId === layer.id) { + tokens[enabledAttribute.typeTokenName] = 'attribute'; + } } var disabledAttributes = this.attributes[programInterface].disabled; for (var j = 0; j < this.attributes[programInterface].disabled.length; j++) { var disabledAttribute = disabledAttributes[j]; - if (disabledAttribute.isLayerConstant !== false && disabledAttribute.layerId !== layer.id) continue; - tokens[disabledAttribute.typeTokenName] = 'uniform'; + if (disabledAttribute.isLayerConstant || disabledAttribute.layerId === layer.id) { + tokens[disabledAttribute.typeTokenName] = 'uniform'; + } } return tokens; @@ -453,7 +456,8 @@ function createAttributes(bucket) { var attribute = interface_.attributes[i]; for (var j = 0; j < bucket.childLayers.length; j++) { var layer = bucket.childLayers[j]; - if (attribute.isLayerConstant !== false && layer.id !== bucket.layer.id) continue; + var isLayerConstant = attribute.isLayerConstant === true || attribute.isLayerConstant === undefined; + if (isLayerConstant && layer.id !== bucket.layer.id) continue; if (isAttributeDisabled(bucket, attribute, layer)) { interfaceAttributes.disabled.push(util.extend({}, attribute, { getValue: createGetAttributeValueMethod(bucket, interfaceName, attribute, j), @@ -461,7 +465,8 @@ function createAttributes(bucket) { programName: 'a_' + attribute.name, layerId: layer.id, layerIndex: j, - typeTokenName: attribute.name + 'Type' + typeTokenName: attribute.name + 'Type', + isLayerConstant: isLayerConstant })); } else { interfaceAttributes.enabled.push(util.extend({}, attribute, { @@ -469,7 +474,8 @@ function createAttributes(bucket) { programName: 'a_' + attribute.name, layerId: layer.id, layerIndex: j, - typeTokenName: attribute.name + 'Type' + typeTokenName: attribute.name + 'Type', + isLayerConstant: isLayerConstant })); } } diff --git a/js/source/worker.js b/js/source/worker.js index 40cd3b5e8bc..c1c6363de39 100644 --- a/js/source/worker.js +++ b/js/source/worker.js @@ -79,10 +79,11 @@ util.extend(Worker.prototype, { } function updateLayer(layer) { + var refLayer = that.layers[layer.ref]; if (that.layers[layer.id]) { - that.layers[layer.id].set(layer); + that.layers[layer.id].set(layer, refLayer); } else { - that.layers[layer.id] = StyleLayer.create(layer, that.layers[layer.ref]); + that.layers[layer.id] = StyleLayer.create(layer, refLayer); } that.layers[layer.id].updatePaintTransitions({}, {transition: false}); } diff --git a/js/style/style.js b/js/style/style.js index b0737b0e93f..532d957c35b 100644 --- a/js/style/style.js +++ b/js/style/style.js @@ -15,6 +15,7 @@ var AnimationLoop = require('./animation_loop'); var validateStyle = require('./validate_style'); var Source = require('../source/source'); var styleSpec = require('./style_spec'); +var StyleFunction = require('./style_function'); module.exports = Style; @@ -544,8 +545,18 @@ Style.prototype = util.inherit(Evented, { if (util.deepEqual(layer.getPaintProperty(name, klass), value)) return this; + var wasFeatureConstant = layer.isPaintValueFeatureConstant(name); layer.setPaintProperty(name, value, klass); + var isFeatureConstant = !(StyleFunction.isFunctionDefinition(value) && value.property !== '$zoom' && value.property !== undefined); + + if (!isFeatureConstant || !wasFeatureConstant) { + this._updates.layers[layerId] = true; + if (layer.source) { + this._updates.sources[layer.source] = true; + } + } + return this.updateClasses(layerId, name); }, From ae06df307c0be1b5808b2e59e26ce87619285016 Mon Sep 17 00:00:00 2001 From: Lucas Wojciechowski Date: Thu, 7 Apr 2016 14:15:02 -0700 Subject: [PATCH 19/32] Optimize layer family creation --- bench/benchmarks/buffer.js | 20 ++++++++++++++++---- js/source/worker.js | 30 +++++++++++++++++++++++++++--- js/source/worker_tile.js | 30 ++++-------------------------- test/js/source/worker_tile.test.js | 20 ++++++++++---------- 4 files changed, 57 insertions(+), 43 deletions(-) diff --git a/bench/benchmarks/buffer.js b/bench/benchmarks/buffer.js index 7ca685d3898..bc8e0481577 100644 --- a/bench/benchmarks/buffer.js +++ b/bench/benchmarks/buffer.js @@ -5,6 +5,7 @@ var Protobuf = require('pbf'); var assert = require('assert'); var WorkerTile = require('../../js/source/worker_tile'); +var Worker = require('../../js/source/worker'); var ajax = require('../../js/util/ajax'); var Style = require('../../js/style/style'); var util = require('../../js/util/util'); @@ -125,9 +126,7 @@ function preloadAssets(stylesheet, callback) { function runSample(stylesheet, getGlyphs, getIcons, getTile, callback) { var timeStart = performance.now(); - var layers = stylesheet.layers.filter(function(layer) { - return !layer.ref && (layer.type === 'fill' || layer.type === 'line' || layer.type === 'circle' || layer.type === 'symbol'); - }); + var layerFamilies = createLayerFamilies(stylesheet.layers); util.asyncAll(coordinates, function(coordinate, eachCallback) { var url = 'https://a.tiles.mapbox.com/v4/mapbox.mapbox-terrain-v2,mapbox.mapbox-streets-v6/' + coordinate.zoom + '/' + coordinate.row + '/' + coordinate.column + '.vector.pbf?access_token=' + config.ACCESS_TOKEN; @@ -159,7 +158,7 @@ function runSample(stylesheet, getGlyphs, getIcons, getTile, callback) { getTile(url, function(err, response) { if (err) throw err; var data = new VT.VectorTile(new Protobuf(new Uint8Array(response))); - workerTile.parse(data, layers, actor, null, function(err) { + workerTile.parse(data, layerFamilies, actor, null, function(err) { if (err) return callback(err); eachCallback(); }); @@ -180,3 +179,16 @@ function asyncTimesSeries(times, work, callback) { callback(); } } + +var createLayerFamiliesCacheKey; +var createLayerFamiliesCacheValue; +function createLayerFamilies(layers) { + if (layers !== createLayerFamiliesCacheKey) { + var worker = new Worker({addEventListener: function() {} }); + worker['set layers'](layers); + + createLayerFamiliesCacheKey = layers; + createLayerFamiliesCacheValue = worker.layerFamilies; + } + return createLayerFamiliesCacheValue; +} diff --git a/js/source/worker.js b/js/source/worker.js index c1c6363de39..7955f199711 100644 --- a/js/source/worker.js +++ b/js/source/worker.js @@ -59,6 +59,7 @@ util.extend(Worker.prototype, { that.layers[styleLayer.id] = styleLayer; } + this.layerFamilies = createLayerFamilies(this.layers); }, 'update layers': function(layers) { @@ -87,6 +88,8 @@ util.extend(Worker.prototype, { } that.layers[layer.id].updatePaintTransitions({}, {transition: false}); } + + this.layerFamilies = createLayerFamilies(this.layers); }, 'load tile': function(params, callback) { @@ -107,7 +110,7 @@ util.extend(Worker.prototype, { if (err) return callback(err); tile.data = new vt.VectorTile(new Protobuf(new Uint8Array(data))); - tile.parse(tile.data, this.layers, this.actor, data, callback); + tile.parse(tile.data, this.layerFamilies, this.actor, data, callback); this.loaded[source] = this.loaded[source] || {}; this.loaded[source][uid] = tile; @@ -119,7 +122,7 @@ util.extend(Worker.prototype, { uid = params.uid; if (loaded && loaded[uid]) { var tile = loaded[uid]; - tile.parse(tile.data, this.layers, this.actor, params.rawTileData, callback); + tile.parse(tile.data, this.layerFamilies, this.actor, params.rawTileData, callback); } }, @@ -211,9 +214,30 @@ util.extend(Worker.prototype, { var geojsonWrapper = new GeoJSONWrapper(geoJSONTile.features); geojsonWrapper.name = '_geojsonTileLayer'; var rawTileData = vtpbf({ layers: { '_geojsonTileLayer': geojsonWrapper }}).buffer; - tile.parse(geojsonWrapper, this.layers, this.actor, rawTileData, callback); + tile.parse(geojsonWrapper, this.layerFamilies, this.actor, rawTileData, callback); } else { return callback(null, null); // nothing in the given tile } } }); + +function createLayerFamilies(layers) { + var families = {}; + + for (var layerId in layers) { + var layer = layers[layerId]; + var parentLayerId = layer.ref || layer.id; + var parentLayer = layers[parentLayerId]; + + if (parentLayer.layout && parentLayer.layout.visibility === 'none') continue; + + families[parentLayerId] = families[parentLayerId] || []; + if (layerId === parentLayerId) { + families[parentLayerId].unshift(layer); + } else { + families[parentLayerId].push(layer); + } + } + + return families; +} diff --git a/js/source/worker_tile.js b/js/source/worker_tile.js index 206aacdb918..e28b648742e 100644 --- a/js/source/worker_tile.js +++ b/js/source/worker_tile.js @@ -20,7 +20,7 @@ function WorkerTile(params) { this.showCollisionBoxes = params.showCollisionBoxes; } -WorkerTile.prototype.parse = function(data, layers, actor, rawTileData, callback) { +WorkerTile.prototype.parse = function(data, layerFamilies, actor, rawTileData, callback) { this.status = 'parsing'; this.data = data; @@ -40,12 +40,10 @@ WorkerTile.prototype.parse = function(data, layers, actor, rawTileData, callback var sourceLayerId; var bucket; - var layerFamilies = createLayerFamilies(this, layers); - // Map non-ref layers to buckets. var bucketIndex = 0; - for (var parentName in layerFamilies) { - layer = layers[parentName]; + for (var layerId in layerFamilies) { + layer = layerFamilies[layerId][0]; if (layer.source !== this.source) continue; if (layer.ref) continue; @@ -57,7 +55,7 @@ WorkerTile.prototype.parse = function(data, layers, actor, rawTileData, callback bucket = Bucket.create({ layer: layer, index: bucketIndex++, - childLayers: layerFamilies[parentName], + childLayers: layerFamilies[layerId], zoom: this.zoom, overscaling: this.overscaling, showCollisionBoxes: this.showCollisionBoxes, @@ -269,23 +267,3 @@ function getTransferables(buckets) { function getLayerId(layer) { return layer.id; } - -function createLayerFamilies(tile, layers) { - var families = {}; - - for (var childId in layers) { - var child = layers[childId]; - var parentId = child.ref || child.id; - var parent = layers[parentId]; - - if (parent.source !== tile.source) continue; - if (parent.minzoom && tile.zoom < parent.minzoom) continue; - if (parent.maxzoom && tile.zoom >= parent.maxzoom) continue; - if (parent.layout && parent.layout.visibility === 'none') continue; - - families[parentId] = families[parentId] || []; - families[parentId].push(child); - } - - return families; -} diff --git a/test/js/source/worker_tile.test.js b/test/js/source/worker_tile.test.js index 353347e74ed..63ae12a19bf 100644 --- a/test/js/source/worker_tile.test.js +++ b/test/js/source/worker_tile.test.js @@ -24,17 +24,17 @@ test('basic', function(t) { }); t.test('basic worker tile', function(t) { - var layers = { - test: new StyleLayer({ + var layerFamilies = { + test: [new StyleLayer({ id: 'test', source: 'source', type: 'circle', layout: {}, compare: function () { return true; } - }) + })] }; - tile.parse(new Wrapper(features), layers, {}, null, function(err, result) { + tile.parse(new Wrapper(features), layerFamilies, {}, null, function(err, result) { t.equal(err, null); t.ok(result.buckets[0]); t.end(); @@ -42,24 +42,24 @@ test('basic', function(t) { }); t.test('hidden layers', function(t) { - var layers = { - 'test': new StyleLayer({ + var layerFamilies = { + 'test': [new StyleLayer({ id: 'test', source: 'source', type: 'circle', layout: {}, compare: function () { return true; } - }), - 'test-hidden': new StyleLayer({ + })], + 'test-hidden': [new StyleLayer({ id: 'test-hidden', source: 'source', type: 'fill', layout: { visibility: 'none' }, compare: function () { return true; } - }) + })] }; - tile.parse(new Wrapper(features), layers, {}, null, function(err, result) { + tile.parse(new Wrapper(features), layerFamilies, {}, null, function(err, result) { t.equal(err, null); t.equal(Object.keys(result.buckets[0].elementGroups).length, 1, 'element groups exclude hidden layer'); t.end(); From 3268906b90c2d5d9e753d5e9c8cf2240b451ed10 Mon Sep 17 00:00:00 2001 From: Lucas Wojciechowski Date: Thu, 7 Apr 2016 16:14:07 -0700 Subject: [PATCH 20/32] Add circle-color property function tests to test-suite --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4eb671842b1..fa2454f8ab3 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ "express": "^4.13.4", "gl": "^2.1.5", "istanbul": "^0.4.2", - "mapbox-gl-test-suite": "mapbox/mapbox-gl-test-suite#d974ec6b3748a258f8ddd7528e049493390177b4", + "mapbox-gl-test-suite": "mapbox/mapbox-gl-test-suite#0b7e7b0d094e16b4568585cd3b746aa0c7ff140b", "nyc": "^6.1.1", "sinon": "^1.15.4", "st": "^1.0.0", From 9101b5fe82008b41a8495500185da8fc64354918 Mon Sep 17 00:00:00 2001 From: Lucas Wojciechowski Date: Thu, 7 Apr 2016 17:23:18 -0700 Subject: [PATCH 21/32] Add unit tests for dds functionality --- js/data/bucket.js | 6 +- test/js/data/bucket.test.js | 114 +++++++++++++++++++++++++++++++--- test/js/source/worker.test.js | 48 ++++++++++++++ 3 files changed, 157 insertions(+), 11 deletions(-) diff --git a/js/data/bucket.js b/js/data/bucket.js index a55b211439a..ca31c3e578e 100644 --- a/js/data/bucket.js +++ b/js/data/bucket.js @@ -466,7 +466,8 @@ function createAttributes(bucket) { layerId: layer.id, layerIndex: j, typeTokenName: attribute.name + 'Type', - isLayerConstant: isLayerConstant + isLayerConstant: isLayerConstant, + components: attribute.components || 1 })); } else { interfaceAttributes.enabled.push(util.extend({}, attribute, { @@ -475,7 +476,8 @@ function createAttributes(bucket) { layerId: layer.id, layerIndex: j, typeTokenName: attribute.name + 'Type', - isLayerConstant: isLayerConstant + isLayerConstant: isLayerConstant, + components: attribute.components || 1 })); } } diff --git a/test/js/data/bucket.test.js b/test/js/data/bucket.test.js index d5d2395ab87..caa76916337 100644 --- a/test/js/data/bucket.test.js +++ b/test/js/data/bucket.test.js @@ -7,7 +7,7 @@ var StyleLayer = require('../../../js/style/style_layer'); test('Bucket', function(t) { - function createClass() { + function createClass(options) { function Class() { Bucket.apply(this, arguments); } @@ -21,17 +21,19 @@ test('Bucket', function(t) { secondElementBuffer: 'testSecondElement', secondElementBufferComponents: 2, - attributeArgs: ['x', 'y'], + attributeArgs: options.attributeArgs || ['x', 'y'], - attributes: [{ + attributes: options.attributes || [{ name: 'map', type: 'Int16', - value: ['x'] + value: ['x'], + isLayerConstant: false }, { name: 'box', components: 2, type: 'Int16', - value: ['x * 2', 'y * 2'] + value: ['x * 2', 'y * 2'], + isLayerConstant: true }] } }; @@ -55,11 +57,18 @@ test('Bucket', function(t) { }; } - function create() { - var Class = createClass(); + function create(options) { + options = options || {}; + + var serializedLayers = (options.layers || [{ id: 'layerid', type: 'circle' }]); + var layers = serializedLayers.map(function(serializedLayer) { + return new StyleLayer(serializedLayer); + }); + + var Class = createClass(options); return new Class({ - layer: new StyleLayer({ id: 'layerid', type: 'circle' }), - childLayers: [new StyleLayer({ id: 'layerid', type: 'circle' })], + layer: layers[0], + childLayers: layers, buffers: {} }); } @@ -93,6 +102,64 @@ test('Bucket', function(t) { t.end(); }); + t.test('add features, multiple layers', function(t) { + var bucket = create({layers: [ + { id: 'one', type: 'circle' }, + { id: 'two', type: 'circle' } + ]}); + + bucket.features = [createFeature(17, 42)]; + bucket.populateBuffers(); + + var v0 = bucket.arrays.testVertex.get(0); + t.equal(v0.one__map, 17); + t.equal(v0.two__map, 17); + t.equal(v0.one__box0, 34); + t.equal(v0.one__box1, 84); + + t.end(); + }); + + t.test('add features, disabled attribute', function(t) { + var bucket = create({ + attributes: [{ + name: 'map', + type: 'Int16', + value: ['5'], + isDisabled: true + }] + }); + + bucket.features = [createFeature(17, 42)]; + bucket.populateBuffers(); + + t.equal(bucket.arrays.testVertex.bytesPerElement, 0); + t.deepEqual( + bucket.attributes.test.disabled[0].getValue.call(bucket), + [5] + ); + + t.end(); + }); + + t.test('add features, array type attribute', function(t) { + var bucket = create({ + attributes: [{ + name: 'map', + type: 'Int16', + value: '[17]' + }] + }); + + bucket.features = [createFeature(17, 42)]; + bucket.populateBuffers(); + + var v0 = bucket.arrays.testVertex.get(0); + t.equal(v0.layerid__map, 17); + + t.end(); + }); + t.test('reset buffers', function(t) { var bucket = create(); @@ -148,5 +215,34 @@ test('Bucket', function(t) { t.end(); }); + t.test('add features', function(t) { + var bucket = create(); + + bucket.features = [createFeature(17, 42)]; + bucket.populateBuffers(); + + var testVertex = bucket.arrays.testVertex; + t.equal(testVertex.length, 1); + var v0 = testVertex.get(0); + t.equal(v0.layerid__map, 17); + t.equal(v0.layerid__box0, 34); + t.equal(v0.layerid__box1, 84); + + var testElement = bucket.arrays.testElement; + t.equal(testElement.length, 1); + var e1 = testElement.get(0); + t.equal(e1.vertices0, 1); + t.equal(e1.vertices1, 2); + t.equal(e1.vertices2, 3); + + var testSecondElement = bucket.arrays.testSecondElement; + t.equal(testSecondElement.length, 1); + var e2 = testSecondElement.get(0); + t.equal(e2.vertices0, 17); + t.equal(e2.vertices1, 42); + + t.end(); + }); + t.end(); }); diff --git a/test/js/source/worker.test.js b/test/js/source/worker.test.js index f2c7e5f30f9..08ed305d5e8 100644 --- a/test/js/source/worker.test.js +++ b/test/js/source/worker.test.js @@ -61,6 +61,54 @@ test('abort tile', function(t) { t.end(); }); +test('set layers', function(t) { + var worker = new Worker(_self); + + worker['set layers']([ + { id: 'one', type: 'circle', paint: { 'circle-color': 'red' } }, + { id: 'two', type: 'circle', paint: { 'circle-color': 'green' } }, + { id: 'three', ref: 'two', type: 'circle', paint: { 'circle-color': 'blue' } } + ]); + + t.equal(worker.layers.one.id, 'one'); + t.equal(worker.layers.two.id, 'two'); + t.equal(worker.layers.three.id, 'three'); + + t.equal(worker.layers.one.getPaintProperty('circle-color'), 'red'); + t.equal(worker.layers.two.getPaintProperty('circle-color'), 'green'); + t.equal(worker.layers.three.getPaintProperty('circle-color'), 'blue'); + + t.equal(worker.layerFamilies.one.length, 1); + t.equal(worker.layerFamilies.one[0].id, 'one'); + t.equal(worker.layerFamilies.two.length, 2); + t.equal(worker.layerFamilies.two[0].id, 'two'); + t.equal(worker.layerFamilies.two[1].id, 'three'); + + t.end(); +}); + +test('update layers', function(t) { + var worker = new Worker(_self); + + worker['set layers']([ + { id: 'one', type: 'circle', paint: { 'circle-color': 'red' } }, + { id: 'two', type: 'circle', paint: { 'circle-color': 'green' } }, + { id: 'three', ref: 'two', type: 'circle', paint: { 'circle-color': 'blue' } } + ]); + + worker['update layers']({ + one: { id: 'one', type: 'circle', paint: { 'circle-color': 'cyan' } }, + two: { id: 'two', type: 'circle', paint: { 'circle-color': 'magenta' } }, + three: { id: 'three', ref: 'two', type: 'circle', paint: { 'circle-color': 'yellow' } } + }); + + t.equal(worker.layers.one.getPaintProperty('circle-color'), 'cyan'); + t.equal(worker.layers.two.getPaintProperty('circle-color'), 'magenta'); + t.equal(worker.layers.three.getPaintProperty('circle-color'), 'yellow'); + + t.end(); +}); + test('remove tile', function(t) { t.test('removes loaded tile', function(t) { var worker = new Worker(_self); From 447739803e753810f4f81677d2c55dece79d3b61 Mon Sep 17 00:00:00 2001 From: Ansis Brammanis Date: Sat, 9 Apr 2016 12:46:14 -0700 Subject: [PATCH 22/32] use glsl macros instead of custom templating Using macros and preprocessor directives is the glsl way of doing this. After this the .glsl files are now valid glsl. I think it's clearer to have both variations explicitly written in glsl than having string replacement. --- js/data/bucket.js | 23 ++++++----------------- js/render/draw_circle.js | 2 +- js/render/painter/use_program.js | 32 +++++++++++++++----------------- shaders/circle.vertex.glsl | 6 +++++- 4 files changed, 27 insertions(+), 36 deletions(-) diff --git a/js/data/bucket.js b/js/data/bucket.js index ca31c3e578e..ce9fed4164d 100644 --- a/js/data/bucket.js +++ b/js/data/bucket.js @@ -328,26 +328,16 @@ Bucket.prototype.recalculateStyleLayers = function() { } }; -Bucket.prototype.getUseProgramTokens = function(programInterface, layer) { - var tokens = {}; - +Bucket.prototype.getProgramMacros = function(programInterface, layer) { + var macros = []; var enabledAttributes = this.attributes[programInterface].enabled; for (var i = 0; i < enabledAttributes.length; i++) { var enabledAttribute = enabledAttributes[i]; - if (enabledAttribute.isLayerConstant || enabledAttribute.layerId === layer.id) { - tokens[enabledAttribute.typeTokenName] = 'attribute'; + if (enabledAttribute.isLayerConstant === false && enabledAttribute.layerId === layer.id) { + macros.push(enabledAttribute.macro); } } - - var disabledAttributes = this.attributes[programInterface].disabled; - for (var j = 0; j < this.attributes[programInterface].disabled.length; j++) { - var disabledAttribute = disabledAttributes[j]; - if (disabledAttribute.isLayerConstant || disabledAttribute.layerId === layer.id) { - tokens[disabledAttribute.typeTokenName] = 'uniform'; - } - } - - return tokens; + return macros; }; var createVertexAddMethodCache = {}; @@ -465,7 +455,6 @@ function createAttributes(bucket) { programName: 'a_' + attribute.name, layerId: layer.id, layerIndex: j, - typeTokenName: attribute.name + 'Type', isLayerConstant: isLayerConstant, components: attribute.components || 1 })); @@ -475,7 +464,7 @@ function createAttributes(bucket) { programName: 'a_' + attribute.name, layerId: layer.id, layerIndex: j, - typeTokenName: attribute.name + 'Type', + macro: 'ATTRIBUTE_' + attribute.name.toUpperCase(), isLayerConstant: isLayerConstant, components: attribute.components || 1 })); diff --git a/js/render/draw_circle.js b/js/render/draw_circle.js index fda2a1e220c..75f5262233d 100644 --- a/js/render/draw_circle.js +++ b/js/render/draw_circle.js @@ -31,7 +31,7 @@ function drawCircles(painter, source, layer, coords) { var elementGroups = bucket.elementGroups.circle; if (!elementGroups) continue; - var program = painter.useProgram('circle', bucket.getUseProgramTokens('circle', layer)); + var program = painter.useProgram('circle', bucket.getProgramMacros('circle', layer)); gl.uniform1f(program.u_blur, Math.max(layer.paint['circle-blur'], antialias)); gl.uniform1f(program.u_size, layer.paint['circle-radius']); diff --git a/js/render/painter/use_program.js b/js/render/painter/use_program.js index a72f729295c..f71954b7f67 100644 --- a/js/render/painter/use_program.js +++ b/js/render/painter/use_program.js @@ -61,19 +61,26 @@ var definitions = { } }; -module.exports._createProgram = function(name, tokens) { +module.exports._createProgram = function(name, macros) { var gl = this.gl; var program = gl.createProgram(); var definition = definitions[name]; + var defines = ''; + if (macros) { + for (var m = 0; m < macros.length; m++) { + defines += '#define ' + macros[m] + '\n'; + } + } + var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER); - gl.shaderSource(fragmentShader, applyTokens(definition.fragmentSource, tokens)); + gl.shaderSource(fragmentShader, defines + definition.fragmentSource); gl.compileShader(fragmentShader); assert(gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS), gl.getShaderInfoLog(fragmentShader)); gl.attachShader(program, fragmentShader); var vertexShader = gl.createShader(gl.VERTEX_SHADER); - gl.shaderSource(vertexShader, applyTokens(definition.vertexSource, tokens)); + gl.shaderSource(vertexShader, defines + definition.vertexSource); gl.compileShader(vertexShader); assert(gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS), gl.getShaderInfoLog(vertexShader)); gl.attachShader(program, vertexShader); @@ -103,19 +110,19 @@ module.exports._createProgram = function(name, tokens) { }, attributes, uniforms); }; -module.exports._createProgramCached = function(name, tokens) { +module.exports._createProgramCached = function(name, macros) { this.cache = this.cache || {}; - var key = JSON.stringify({name: name, tokens: tokens}); + var key = JSON.stringify({name: name, macros: macros}); if (!this.cache[key]) { - this.cache[key] = this._createProgram(name, tokens); + this.cache[key] = this._createProgram(name, macros); } return this.cache[key]; }; -module.exports.useProgram = function (nextProgramName, tokens) { +module.exports.useProgram = function (nextProgramName, macros) { var gl = this.gl; - var nextProgram = this._createProgramCached(nextProgramName, tokens); + var nextProgram = this._createProgramCached(nextProgramName, macros); var previousProgram = this.currentProgram; if (previousProgram !== nextProgram) { @@ -141,12 +148,3 @@ module.exports.useProgram = function (nextProgramName, tokens) { return nextProgram; }; - -function applyTokens(string, tokens) { - tokens = tokens || {}; - for (var key in tokens) { - var value = tokens[key]; - string = string.replace('{{' + key + '}}', value); - } - return string; -} diff --git a/shaders/circle.vertex.glsl b/shaders/circle.vertex.glsl index d47bb493d62..906cd8cb219 100644 --- a/shaders/circle.vertex.glsl +++ b/shaders/circle.vertex.glsl @@ -6,7 +6,11 @@ uniform mediump float u_size; attribute vec2 a_pos; -{{colorType}} lowp vec4 a_color; +#ifdef ATTRIBUTE_COLOR +attribute lowp vec4 a_color; +#else +uniform lowp vec4 a_color; +#endif varying vec2 v_extrude; varying lowp vec4 v_color; From 7c5369c15d38ff22092cb64a417d1e135cdd6e0a Mon Sep 17 00:00:00 2001 From: Lucas Wojciechowski Date: Mon, 11 Apr 2016 14:43:55 -0700 Subject: [PATCH 23/32] Fix StyleDeclaration#isFeatureConstant --- js/style/style_declaration.js | 13 ++++++------- test/js/style/style_declaration.test.js | 16 ++++++++++++++++ 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/js/style/style_declaration.js b/js/style/style_declaration.js index 97b120dde14..8b6ef6abcd0 100644 --- a/js/style/style_declaration.js +++ b/js/style/style_declaration.js @@ -15,13 +15,12 @@ function StyleDeclaration(reference, value) { this.json = JSON.stringify(this.value); var parsedValue = this.type === 'color' ? parseColor(this.value) : value; - if (reference.function === 'interpolated') { - this.calculate = MapboxGLFunction.interpolated(parsedValue); - } else { - this.calculate = MapboxGLFunction['piecewise-constant'](parsedValue); - if (reference.transition) { - this.calculate = transitioned(this.calculate); - } + this.calculate = MapboxGLFunction[reference.function || 'piecewise-constant'](parsedValue); + this.isFeatureConstant = this.calculate.isFeatureConstant; + this.isGlobalConstant = this.calculate.isGlobalConstant; + + if (reference.function === 'piecewise-constant' && reference.transition) { + this.calculate = transitioned(this.calculate); } } diff --git a/test/js/style/style_declaration.test.js b/test/js/style/style_declaration.test.js index aea9ec1958f..9f0843947f3 100644 --- a/test/js/style/style_declaration.test.js +++ b/test/js/style/style_declaration.test.js @@ -7,6 +7,8 @@ test('StyleDeclaration', function(t) { t.test('constant', function(t) { t.equal((new StyleDeclaration({type: "number"}, 5)).calculate({zoom: 0}), 5); t.equal((new StyleDeclaration({type: "number"}, 5)).calculate({zoom: 100}), 5); + t.ok((new StyleDeclaration({type: "number"}, 5)).isFeatureConstant); + t.ok((new StyleDeclaration({type: "number"}, 5)).isGlobalConstant); t.end(); }); @@ -20,6 +22,8 @@ test('StyleDeclaration', function(t) { t.equal((new StyleDeclaration(reference, { stops: [[0, 0], [1, 10], [2, 20]] })).calculate({zoom: 2}), 20); t.equal((new StyleDeclaration(reference, { stops: [[0, 0], [1, 10], [2, 20]] })).calculate({zoom: 1}), 10); t.equal((new StyleDeclaration(reference, { stops: [[0, 0]] })).calculate({zoom: 6}), 0); + t.ok((new StyleDeclaration(reference, { stops: [[0, 1]] })).isFeatureConstant); + t.notOk((new StyleDeclaration(reference, { stops: [[0, 1]] })).isGlobalConstant); t.end(); }); @@ -69,5 +73,17 @@ test('StyleDeclaration', function(t) { t.end(); }); + t.test('property functions', function(t) { + var declaration = new StyleDeclaration( + {type: "number", function: "interpolated"}, + { stops: [[0, 1]], property: 'mapbox' } + ); + + t.notOk(declaration.isFeatureConstant); + t.notOk(declaration.isGlobalConstant); + + t.end(); + }); + t.end(); }); From 9ae3e92cf73de4c350b93250f6c1c435fce1ba22 Mon Sep 17 00:00:00 2001 From: Lucas Wojciechowski Date: Mon, 11 Apr 2016 14:58:18 -0700 Subject: [PATCH 24/32] Enabled data-driven styling for circle-radius --- js/data/bucket/circle_bucket.js | 10 ++++++++++ js/render/draw_circle.js | 10 ++-------- shaders/circle.fragment.glsl | 3 ++- shaders/circle.vertex.glsl | 16 ++++++++++++++-- 4 files changed, 28 insertions(+), 11 deletions(-) diff --git a/js/data/bucket/circle_bucket.js b/js/data/bucket/circle_bucket.js index 1d293aa370d..fee8fcc6298 100644 --- a/js/data/bucket/circle_bucket.js +++ b/js/data/bucket/circle_bucket.js @@ -53,6 +53,16 @@ CircleBucket.prototype.programInterfaces = { layer.isPaintValueFeatureConstant('circle-opacity') ); } + }, { + name: 'radius', + components: 1, + type: 'Uint16', + isLayerConstant: false, + value: ['layer.getPaintValue("circle-radius", globalProperties, featureProperties)'], + multiplier: 10, + isDisabled: function(layer) { + return layer.isPaintValueFeatureConstant("circle-radius"); + } }] } }; diff --git a/js/render/draw_circle.js b/js/render/draw_circle.js index 75f5262233d..c3a4260e639 100644 --- a/js/render/draw_circle.js +++ b/js/render/draw_circle.js @@ -16,12 +16,6 @@ function drawCircles(painter, source, layer, coords) { // large circles are not clipped to tiles gl.disable(gl.STENCIL_TEST); - // antialiasing factor: this is a minimum blur distance that serves as - // a faux-antialiasing for the circle. since blur is a ratio of the circle's - // size and the intent is to keep the blur at roughly 1px, the two - // are inversely related. - var antialias = 1 / browser.devicePixelRatio / layer.paint['circle-radius']; - for (var i = 0; i < coords.length; i++) { var coord = coords[i]; @@ -33,8 +27,8 @@ function drawCircles(painter, source, layer, coords) { var program = painter.useProgram('circle', bucket.getProgramMacros('circle', layer)); - gl.uniform1f(program.u_blur, Math.max(layer.paint['circle-blur'], antialias)); - gl.uniform1f(program.u_size, layer.paint['circle-radius']); + gl.uniform1f(program.u_blur, layer.paint['circle-blur']); + gl.uniform1f(program.u_devicepixelratio, browser.devicePixelRatio); painter.setPosMatrix(painter.translatePosMatrix( coord.posMatrix, diff --git a/shaders/circle.fragment.glsl b/shaders/circle.fragment.glsl index 4874a024abc..1d9936199e3 100644 --- a/shaders/circle.fragment.glsl +++ b/shaders/circle.fragment.glsl @@ -4,8 +4,9 @@ uniform lowp float u_blur; varying lowp vec4 v_color; varying vec2 v_extrude; +varying lowp float v_antialiasblur; void main() { - float t = smoothstep(1.0 - u_blur, 1.0, length(v_extrude)); + float t = smoothstep(1.0 - max(u_blur, v_antialiasblur), 1.0, length(v_extrude)); gl_FragColor = v_color * (1.0 - t); } diff --git a/shaders/circle.vertex.glsl b/shaders/circle.vertex.glsl index 906cd8cb219..a0457fc293a 100644 --- a/shaders/circle.vertex.glsl +++ b/shaders/circle.vertex.glsl @@ -2,7 +2,7 @@ precision highp float; uniform mat4 u_matrix; uniform mat4 u_exmatrix; -uniform mediump float u_size; +uniform float u_devicepixelratio; attribute vec2 a_pos; @@ -12,14 +12,21 @@ attribute lowp vec4 a_color; uniform lowp vec4 a_color; #endif +#ifdef ATTRIBUTE_RADIUS +attribute mediump float a_radius; +#else +uniform mediump float a_radius; +#endif + varying vec2 v_extrude; varying lowp vec4 v_color; +varying lowp float v_antialiasblur; void main(void) { // unencode the extrusion vector that we snuck into the a_pos vector v_extrude = vec2(mod(a_pos, 2.0) * 2.0 - 1.0); - vec4 extrude = u_exmatrix * vec4(v_extrude * u_size, 0, 0); + vec4 extrude = u_exmatrix * vec4(v_extrude * a_radius / 10.0, 0, 0); // multiply a_pos by 0.5, since we had it * 2 in order to sneak // in extrusion data gl_Position = u_matrix * vec4(floor(a_pos * 0.5), 0, 1); @@ -29,4 +36,9 @@ void main(void) { gl_Position += extrude * gl_Position.w; v_color = a_color / 255.0; + + // This is a minimum blur distance that serves as a faux-antialiasing for + // the circle. since blur is a ratio of the circle's size and the intent is + // to keep the blur at roughly 1px, the two are inversely related. + v_antialiasblur = 1.0 / u_devicepixelratio / (a_radius / 10.0); } From 41f6561a9c86e064a2622713f828ed4dd3c8155e Mon Sep 17 00:00:00 2001 From: Ansis Brammanis Date: Fri, 8 Apr 2016 20:51:04 -0700 Subject: [PATCH 25/32] disable attributes based on their paint property This replaces a per-attribute `isDisabled` function with paint property key. All data-driven attributes must now depend on only a single paint property. The `a_color` attribute used to depend on both the `circle-color` and `circle-opacity` paint properties. This splits them up into two attributes/uniforms, each of which only depends on one paint property. I think it will be simpler to have all attributes be based on a single style property. We will need to split up: - a_color, which depends on -color and -opacity - u_linewidth, which depends on line-width and line-width-gap --- js/data/bucket.js | 13 ++++--------- js/data/bucket/circle_bucket.js | 18 +++--------------- js/render/draw_circle.js | 1 + shaders/circle.fragment.glsl | 3 ++- test/js/data/bucket.test.js | 2 +- 5 files changed, 11 insertions(+), 26 deletions(-) diff --git a/js/data/bucket.js b/js/data/bucket.js index ce9fed4164d..d764ead338d 100644 --- a/js/data/bucket.js +++ b/js/data/bucket.js @@ -448,7 +448,7 @@ function createAttributes(bucket) { var layer = bucket.childLayers[j]; var isLayerConstant = attribute.isLayerConstant === true || attribute.isLayerConstant === undefined; if (isLayerConstant && layer.id !== bucket.layer.id) continue; - if (isAttributeDisabled(bucket, attribute, layer)) { + if (isAttributeDisabled(attribute, layer)) { interfaceAttributes.disabled.push(util.extend({}, attribute, { getValue: createGetAttributeValueMethod(bucket, interfaceName, attribute, j), name: layer.id + '__' + attribute.name, @@ -476,12 +476,7 @@ function createAttributes(bucket) { } -function isAttributeDisabled(bucket, attribute, layer) { - if (attribute.isDisabled === undefined || attribute.isDisabled === false) { - return false; - } else if (attribute.isDisabled === true) { - return true; - } else { - return !!attribute.isDisabled.call(bucket, layer); - } +function isAttributeDisabled(attribute, layer) { + return attribute.paintProperty !== undefined && + layer.isPaintValueFeatureConstant(attribute.paintProperty); } diff --git a/js/data/bucket/circle_bucket.js b/js/data/bucket/circle_bucket.js index fee8fcc6298..232359fab24 100644 --- a/js/data/bucket/circle_bucket.js +++ b/js/data/bucket/circle_bucket.js @@ -40,19 +40,9 @@ CircleBucket.prototype.programInterfaces = { components: 4, type: 'Uint8', isLayerConstant: false, - value: ( - 'this._premultiplyColor(' + - 'layer.getPaintValue("circle-color", globalProperties, featureProperties),' + - 'layer.getPaintValue("circle-opacity", globalProperties, featureProperties)' + - ')' - ), + value: 'this._premultiplyColor(layer.getPaintValue("circle-color", globalProperties, featureProperties), 1)', multiplier: 255, - isDisabled: function(layer) { - return ( - layer.isPaintValueFeatureConstant("circle-color") && - layer.isPaintValueFeatureConstant('circle-opacity') - ); - } + paintProperty: 'circle-color' }, { name: 'radius', components: 1, @@ -60,9 +50,7 @@ CircleBucket.prototype.programInterfaces = { isLayerConstant: false, value: ['layer.getPaintValue("circle-radius", globalProperties, featureProperties)'], multiplier: 10, - isDisabled: function(layer) { - return layer.isPaintValueFeatureConstant("circle-radius"); - } + paintProperty: 'circle-radius' }] } }; diff --git a/js/render/draw_circle.js b/js/render/draw_circle.js index c3a4260e639..cfed8df0dc7 100644 --- a/js/render/draw_circle.js +++ b/js/render/draw_circle.js @@ -29,6 +29,7 @@ function drawCircles(painter, source, layer, coords) { gl.uniform1f(program.u_blur, layer.paint['circle-blur']); gl.uniform1f(program.u_devicepixelratio, browser.devicePixelRatio); + gl.uniform1f(program.u_opacity, layer.paint['circle-opacity']); painter.setPosMatrix(painter.translatePosMatrix( coord.posMatrix, diff --git a/shaders/circle.fragment.glsl b/shaders/circle.fragment.glsl index 1d9936199e3..581f2e716f0 100644 --- a/shaders/circle.fragment.glsl +++ b/shaders/circle.fragment.glsl @@ -1,6 +1,7 @@ precision mediump float; uniform lowp float u_blur; +uniform lowp float u_opacity; varying lowp vec4 v_color; varying vec2 v_extrude; @@ -8,5 +9,5 @@ varying lowp float v_antialiasblur; void main() { float t = smoothstep(1.0 - max(u_blur, v_antialiasblur), 1.0, length(v_extrude)); - gl_FragColor = v_color * (1.0 - t); + gl_FragColor = v_color * (1.0 - t) * u_opacity; } diff --git a/test/js/data/bucket.test.js b/test/js/data/bucket.test.js index caa76916337..698fb7ba2b4 100644 --- a/test/js/data/bucket.test.js +++ b/test/js/data/bucket.test.js @@ -126,7 +126,7 @@ test('Bucket', function(t) { name: 'map', type: 'Int16', value: ['5'], - isDisabled: true + paintProperty: 'circle-color' }] }); From 5a08b97f61189077c3303caf83e1c4dce2ba8c84 Mon Sep 17 00:00:00 2001 From: Ansis Brammanis Date: Mon, 11 Apr 2016 13:10:02 -0700 Subject: [PATCH 26/32] replace .isLayerConstant with .paintProperty --- js/data/bucket.js | 19 +++++------------- js/data/bucket/circle_bucket.js | 1 - test/js/data/bucket.test.js | 34 +++++++++++++++++++++++++-------- 3 files changed, 31 insertions(+), 23 deletions(-) diff --git a/js/data/bucket.js b/js/data/bucket.js index d764ead338d..abc60d3ee73 100644 --- a/js/data/bucket.js +++ b/js/data/bucket.js @@ -223,7 +223,7 @@ Bucket.prototype.setAttribPointers = function(programName, gl, program, offset, var disabledAttributes = this.attributes[programName].disabled; for (var i = 0; i < disabledAttributes.length; i++) { attribute = disabledAttributes[i]; - if (attribute.isLayerConstant === false && layer.id !== attribute.layerId) continue; + if (layer.id !== attribute.layerId) continue; var attributeId = program[attribute.programName]; gl['uniform' + attribute.components + 'fv'](attributeId, attribute.getValue.apply(this, args)); @@ -235,7 +235,7 @@ Bucket.prototype.setAttribPointers = function(programName, gl, program, offset, for (var j = 0; j < enabledAttributes.length; j++) { attribute = enabledAttributes[j]; - if (attribute.isLayerConstant === false && layer.id !== attribute.layerId) continue; + if (attribute.paintProperty && layer.id !== attribute.layerId) continue; if (!getMember(attribute.name)) continue; gl.vertexAttribPointer( @@ -333,7 +333,7 @@ Bucket.prototype.getProgramMacros = function(programInterface, layer) { var enabledAttributes = this.attributes[programInterface].enabled; for (var i = 0; i < enabledAttributes.length; i++) { var enabledAttribute = enabledAttributes[i]; - if (enabledAttribute.isLayerConstant === false && enabledAttribute.layerId === layer.id) { + if (enabledAttribute.paintProperty && enabledAttribute.layerId === layer.id) { macros.push(enabledAttribute.macro); } } @@ -446,16 +446,14 @@ function createAttributes(bucket) { var attribute = interface_.attributes[i]; for (var j = 0; j < bucket.childLayers.length; j++) { var layer = bucket.childLayers[j]; - var isLayerConstant = attribute.isLayerConstant === true || attribute.isLayerConstant === undefined; - if (isLayerConstant && layer.id !== bucket.layer.id) continue; - if (isAttributeDisabled(attribute, layer)) { + if (!attribute.paintProperty && layer.id !== bucket.layer.id) continue; + if (attribute.paintProperty && layer.isPaintValueFeatureConstant(attribute.paintProperty)) { interfaceAttributes.disabled.push(util.extend({}, attribute, { getValue: createGetAttributeValueMethod(bucket, interfaceName, attribute, j), name: layer.id + '__' + attribute.name, programName: 'a_' + attribute.name, layerId: layer.id, layerIndex: j, - isLayerConstant: isLayerConstant, components: attribute.components || 1 })); } else { @@ -465,7 +463,6 @@ function createAttributes(bucket) { layerId: layer.id, layerIndex: j, macro: 'ATTRIBUTE_' + attribute.name.toUpperCase(), - isLayerConstant: isLayerConstant, components: attribute.components || 1 })); } @@ -474,9 +471,3 @@ function createAttributes(bucket) { } return attributes; } - - -function isAttributeDisabled(attribute, layer) { - return attribute.paintProperty !== undefined && - layer.isPaintValueFeatureConstant(attribute.paintProperty); -} diff --git a/js/data/bucket/circle_bucket.js b/js/data/bucket/circle_bucket.js index 232359fab24..003ba2ef830 100644 --- a/js/data/bucket/circle_bucket.js +++ b/js/data/bucket/circle_bucket.js @@ -39,7 +39,6 @@ CircleBucket.prototype.programInterfaces = { name: 'color', components: 4, type: 'Uint8', - isLayerConstant: false, value: 'this._premultiplyColor(layer.getPaintValue("circle-color", globalProperties, featureProperties), 1)', multiplier: 255, paintProperty: 'circle-color' diff --git a/test/js/data/bucket.test.js b/test/js/data/bucket.test.js index 698fb7ba2b4..b59170d26d5 100644 --- a/test/js/data/bucket.test.js +++ b/test/js/data/bucket.test.js @@ -27,13 +27,12 @@ test('Bucket', function(t) { name: 'map', type: 'Int16', value: ['x'], - isLayerConstant: false + paintProperty: 'circle-color' }, { name: 'box', components: 2, type: 'Int16', - value: ['x * 2', 'y * 2'], - isLayerConstant: true + value: ['x * 2', 'y * 2'] }] } }; @@ -57,14 +56,30 @@ test('Bucket', function(t) { }; } + var dataDrivenPaint = { + 'circle-color': { + stops: [[0, 'red'], [100, 'violet']], + property: 'mapbox' + } + }; + + var constantPaint = {}; + function create(options) { options = options || {}; - var serializedLayers = (options.layers || [{ id: 'layerid', type: 'circle' }]); + var serializedLayers = (options.layers || [{ + id: 'layerid', + type: 'circle', + paint: dataDrivenPaint + }]); var layers = serializedLayers.map(function(serializedLayer) { - return new StyleLayer(serializedLayer); + var styleLayer = new StyleLayer(serializedLayer); + styleLayer.updatePaintTransitions([], {}, {}); + return styleLayer; }); + var Class = createClass(options); return new Class({ layer: layers[0], @@ -104,8 +119,8 @@ test('Bucket', function(t) { t.test('add features, multiple layers', function(t) { var bucket = create({layers: [ - { id: 'one', type: 'circle' }, - { id: 'two', type: 'circle' } + { id: 'one', type: 'circle', paint: dataDrivenPaint }, + { id: 'two', type: 'circle', paint: dataDrivenPaint } ]}); bucket.features = [createFeature(17, 42)]; @@ -127,7 +142,10 @@ test('Bucket', function(t) { type: 'Int16', value: ['5'], paintProperty: 'circle-color' - }] + }], + layers: [ + { id: 'one', type: 'circle', paint: constantPaint } + ] }); bucket.features = [createFeature(17, 42)]; From 1f5afa7db08a2bdcc492a46e500ff5d020e6f4a2 Mon Sep 17 00:00:00 2001 From: Ansis Brammanis Date: Mon, 11 Apr 2016 14:31:48 -0700 Subject: [PATCH 27/32] separate u_opacity from u_color This prepares the shaders for the switch to data driven styling where opacity and color need to be passed to the shader independent of eachotehr. remove the `additionalOpacity` parameter from `util.premultiply` --- js/data/bucket/circle_bucket.js | 2 +- js/render/draw_background.js | 3 ++- js/render/draw_fill.js | 11 +++++++---- js/render/draw_line.js | 4 +++- js/render/draw_symbol.js | 6 ++++-- js/util/struct_array.js | 1 + js/util/util.js | 6 ++---- shaders/fill.fragment.glsl | 3 ++- shaders/line.fragment.glsl | 3 ++- shaders/linesdfpattern.fragment.glsl | 3 ++- shaders/outline.fragment.glsl | 3 ++- shaders/sdf.fragment.glsl | 3 ++- test/js/util/util.test.js | 2 +- 13 files changed, 31 insertions(+), 19 deletions(-) diff --git a/js/data/bucket/circle_bucket.js b/js/data/bucket/circle_bucket.js index 003ba2ef830..28ccb920e0c 100644 --- a/js/data/bucket/circle_bucket.js +++ b/js/data/bucket/circle_bucket.js @@ -39,7 +39,7 @@ CircleBucket.prototype.programInterfaces = { name: 'color', components: 4, type: 'Uint8', - value: 'this._premultiplyColor(layer.getPaintValue("circle-color", globalProperties, featureProperties), 1)', + value: 'this._premultiplyColor(layer.getPaintValue("circle-color", globalProperties, featureProperties))', multiplier: 255, paintProperty: 'circle-color' }, { diff --git a/js/render/draw_background.js b/js/render/draw_background.js index 809264c349a..577bf888f06 100644 --- a/js/render/draw_background.js +++ b/js/render/draw_background.js @@ -10,7 +10,7 @@ module.exports = drawBackground; function drawBackground(painter, source, layer) { var gl = painter.gl; var transform = painter.transform; - var color = util.premultiply(layer.paint['background-color'], layer.paint['background-opacity']); + var color = util.premultiply(layer.paint['background-color']); var image = layer.paint['background-pattern']; var opacity = layer.paint['background-opacity']; var program; @@ -42,6 +42,7 @@ function drawBackground(painter, source, layer) { program = painter.useProgram('fill'); gl.uniform4fv(program.u_color, color); + gl.uniform1f(program.u_opacity, opacity); } gl.disable(gl.STENCIL_TEST); diff --git a/js/render/draw_fill.js b/js/render/draw_fill.js index de108fdcb15..b641ea62faf 100644 --- a/js/render/draw_fill.js +++ b/js/render/draw_fill.js @@ -9,12 +9,13 @@ function draw(painter, source, layer, coords) { var gl = painter.gl; gl.enable(gl.STENCIL_TEST); - var color = util.premultiply(layer.paint['fill-color'], layer.paint['fill-opacity']); + var color = util.premultiply(layer.paint['fill-color']); var image = layer.paint['fill-pattern']; - var strokeColor = util.premultiply(layer.paint['fill-outline-color'], layer.paint['fill-opacity']); + var strokeColor = util.premultiply(layer.paint['fill-outline-color']); + var opacity = layer.paint['fill-opacity']; // Draw fill - if (image ? !painter.isOpaquePass : painter.isOpaquePass === (color[3] === 1)) { + if (image ? !painter.isOpaquePass : painter.isOpaquePass === (color[3] === 1 && opacity === 1)) { // Once we switch to earcut drawing we can pull most of the WebGL setup // outside of this coords loop. for (var i = 0; i < coords.length; i++) { @@ -44,6 +45,7 @@ function draw(painter, source, layer, coords) { } gl.uniform2f(outlineProgram.u_world, gl.drawingBufferWidth, gl.drawingBufferHeight); gl.uniform4fv(outlineProgram.u_color, strokeColor ? strokeColor : color); + gl.uniform1f(outlineProgram.u_opacity, opacity); for (var j = 0; j < coords.length; j++) { drawStroke(painter, source, layer, coords[j]); @@ -77,7 +79,7 @@ function drawFill(painter, source, layer, coord) { var gl = painter.gl; - var color = util.premultiply(layer.paint['fill-color'], layer.paint['fill-opacity']); + var color = util.premultiply(layer.paint['fill-color']); var image = layer.paint['fill-pattern']; var opacity = layer.paint['fill-opacity']; @@ -145,6 +147,7 @@ function drawFill(painter, source, layer, coord) { // Draw filling rectangle. program = painter.useProgram('fill'); gl.uniform4fv(fillProgram.u_color, color); + gl.uniform1f(fillProgram.u_opacity, opacity); } painter.setPosMatrix(posMatrix); diff --git a/js/render/draw_line.js b/js/render/draw_line.js index 0ff7e30a859..9dc26de15a6 100644 --- a/js/render/draw_line.js +++ b/js/render/draw_line.js @@ -45,7 +45,7 @@ module.exports = function drawLine(painter, source, layer, coords) { } var outset = offset + edgeWidth + antialiasing / 2 + shift; - var color = util.premultiply(layer.paint['line-color'], layer.paint['line-opacity']); + var color = util.premultiply(layer.paint['line-color']); var tr = painter.transform; @@ -69,6 +69,7 @@ module.exports = function drawLine(painter, source, layer, coords) { gl.uniform2fv(program.u_linewidth, [ outset, inset ]); gl.uniform1f(program.u_blur, blur); gl.uniform4fv(program.u_color, color); + gl.uniform1f(program.u_opacity, layer.paint['line-opacity']); posA = painter.lineAtlas.getDash(dasharray.from, layer.layout['line-cap'] === 'round'); posB = painter.lineAtlas.getDash(dasharray.to, layer.layout['line-cap'] === 'round'); @@ -116,6 +117,7 @@ module.exports = function drawLine(painter, source, layer, coords) { gl.uniform1f(program.u_offset, -layer.paint['line-offset']); gl.uniformMatrix2fv(program.u_antialiasingmatrix, false, antialiasingMatrix); gl.uniform4fv(program.u_color, color); + gl.uniform1f(program.u_opacity, layer.paint['line-opacity']); } for (var k = 0; k < coords.length; k++) { diff --git a/js/render/draw_symbol.js b/js/render/draw_symbol.js index 94cb54cbc26..5a928af7aaf 100644 --- a/js/render/draw_symbol.js +++ b/js/render/draw_symbol.js @@ -154,11 +154,12 @@ function drawSymbol(painter, layer, posMatrix, tile, bucket, elementGroups, pref var gamma = 0.105 * defaultSizes[prefix] / fontSize / browser.devicePixelRatio; if (layer.paint[prefix + '-halo-width']) { - var haloColor = util.premultiply(layer.paint[prefix + '-halo-color'], layer.paint[prefix + '-opacity']); + var haloColor = util.premultiply(layer.paint[prefix + '-halo-color']); // Draw halo underneath the text. gl.uniform1f(program.u_gamma, (layer.paint[prefix + '-halo-blur'] * blurOffset / fontScale / sdfPx + gamma) * gammaScale); gl.uniform4fv(program.u_color, haloColor); + gl.uniform1f(program.u_opacity, layer.paint[prefix + '-opacity']); gl.uniform1f(program.u_buffer, (haloOffset - layer.paint[prefix + '-halo-width'] / fontScale) / sdfPx); for (var j = 0; j < elementGroups.length; j++) { @@ -170,9 +171,10 @@ function drawSymbol(painter, layer, posMatrix, tile, bucket, elementGroups, pref } } - var color = util.premultiply(layer.paint[prefix + '-color'], layer.paint[prefix + '-opacity']); + var color = util.premultiply(layer.paint[prefix + '-color']); gl.uniform1f(program.u_gamma, gamma * gammaScale); gl.uniform4fv(program.u_color, color); + gl.uniform1f(program.u_opacity, layer.paint[prefix + '-opacity']); gl.uniform1f(program.u_buffer, (256 - 64) / 256); for (var i = 0; i < elementGroups.length; i++) { diff --git a/js/util/struct_array.js b/js/util/struct_array.js index 17582746d5a..8cfe39d3156 100644 --- a/js/util/struct_array.js +++ b/js/util/struct_array.js @@ -116,6 +116,7 @@ function StructArrayType(options) { function StructArrayType() { StructArray.apply(this, arguments); + this.members = StructType.prototype.members; } StructArrayType.serialize = serializeStructArrayType; diff --git a/js/util/util.js b/js/util/util.js index b84c9aa3f3f..d35aa0b4ba9 100644 --- a/js/util/util.js +++ b/js/util/util.js @@ -55,14 +55,12 @@ exports.ease = exports.bezier(0.25, 0.1, 0.25, 1); * by the A (alpha) component * * @param {Array} color color array - * @param {number} [additionalOpacity] additional opacity to be multiplied into - * the color's alpha component. * @returns {Array} premultiplied color array * @private */ -exports.premultiply = function (color, additionalOpacity) { +exports.premultiply = function (color) { if (!color) return null; - var opacity = color[3] * additionalOpacity; + var opacity = color[3]; return [ color[0] * opacity, color[1] * opacity, diff --git a/shaders/fill.fragment.glsl b/shaders/fill.fragment.glsl index e8cc2e202aa..87101ecedd4 100644 --- a/shaders/fill.fragment.glsl +++ b/shaders/fill.fragment.glsl @@ -1,7 +1,8 @@ precision mediump float; uniform lowp vec4 u_color; +uniform lowp float u_opacity; void main() { - gl_FragColor = u_color; + gl_FragColor = u_color * u_opacity; } diff --git a/shaders/line.fragment.glsl b/shaders/line.fragment.glsl index 3578f9bfadd..f0ff35b247f 100644 --- a/shaders/line.fragment.glsl +++ b/shaders/line.fragment.glsl @@ -2,6 +2,7 @@ precision mediump float; uniform vec2 u_linewidth; uniform lowp vec4 u_color; +uniform lowp float u_opacity; uniform float u_blur; varying vec2 v_normal; @@ -18,5 +19,5 @@ void main() { float blur = u_blur * v_gamma_scale; float alpha = clamp(min(dist - (u_linewidth.t - blur), u_linewidth.s - dist) / blur, 0.0, 1.0); - gl_FragColor = u_color * alpha; + gl_FragColor = u_color * (alpha * u_opacity); } diff --git a/shaders/linesdfpattern.fragment.glsl b/shaders/linesdfpattern.fragment.glsl index c79cd02a946..5548d21fba1 100644 --- a/shaders/linesdfpattern.fragment.glsl +++ b/shaders/linesdfpattern.fragment.glsl @@ -2,6 +2,7 @@ precision mediump float; uniform vec2 u_linewidth; uniform lowp vec4 u_color; +uniform lowp float u_opacity; uniform float u_blur; uniform sampler2D u_image; uniform float u_sdfgamma; @@ -27,5 +28,5 @@ void main() { float sdfdist = mix(sdfdist_a, sdfdist_b, u_mix); alpha *= smoothstep(0.5 - u_sdfgamma, 0.5 + u_sdfgamma, sdfdist); - gl_FragColor = u_color * alpha; + gl_FragColor = u_color * (alpha * u_opacity); } diff --git a/shaders/outline.fragment.glsl b/shaders/outline.fragment.glsl index f4dec896174..344014dcaa3 100644 --- a/shaders/outline.fragment.glsl +++ b/shaders/outline.fragment.glsl @@ -1,11 +1,12 @@ precision mediump float; uniform lowp vec4 u_color; +uniform lowp float u_opacity; varying vec2 v_pos; void main() { float dist = length(v_pos - gl_FragCoord.xy); float alpha = smoothstep(1.0, 0.0, dist); - gl_FragColor = u_color * alpha; + gl_FragColor = u_color * (alpha * u_opacity); } diff --git a/shaders/sdf.fragment.glsl b/shaders/sdf.fragment.glsl index 43c406dcda0..a9e7e1234a1 100644 --- a/shaders/sdf.fragment.glsl +++ b/shaders/sdf.fragment.glsl @@ -3,6 +3,7 @@ precision mediump float; uniform sampler2D u_texture; uniform sampler2D u_fadetexture; uniform lowp vec4 u_color; +uniform lowp float u_opacity; uniform lowp float u_buffer; uniform lowp float u_gamma; @@ -15,5 +16,5 @@ void main() { lowp float fade_alpha = texture2D(u_fadetexture, v_fade_tex).a; lowp float gamma = u_gamma * v_gamma_scale; lowp float alpha = smoothstep(u_buffer - gamma, u_buffer + gamma, dist) * fade_alpha; - gl_FragColor = u_color * alpha; + gl_FragColor = u_color * (alpha * u_opacity); } diff --git a/test/js/util/util.test.js b/test/js/util/util.test.js index a88f4fea0a7..23e17e8cc81 100644 --- a/test/js/util/util.test.js +++ b/test/js/util/util.test.js @@ -9,7 +9,7 @@ test('util', function(t) { t.equal(util.easeCubicInOut(0.2), 0.03200000000000001); t.equal(util.easeCubicInOut(0.5), 0.5, 'easeCubicInOut=0.5'); t.equal(util.easeCubicInOut(1), 1, 'easeCubicInOut=1'); - t.deepEqual(util.premultiply([0, 0.5, 1, 0.5], 0.5), [0, 0.125, 0.25, 0.25], 'premultiply'); + t.deepEqual(util.premultiply([0, 0.5, 1, 0.5]), [0, 0.25, 0.5, 0.5], 'premultiply'); t.deepEqual(util.keysDifference({a:1}, {}), ['a'], 'keysDifference'); t.deepEqual(util.keysDifference({a:1}, {a:1}), [], 'keysDifference'); t.deepEqual(util.extend({a:1}, {b:2}), {a:1, b:2}, 'extend'); From d4208bf1585d70c29017a47a6b00ed5ff4d48c54 Mon Sep 17 00:00:00 2001 From: Ansis Brammanis Date: Mon, 11 Apr 2016 16:44:23 -0700 Subject: [PATCH 28/32] use function instead of string of code for getValue A function of the layer, globalProperties, featureProperties is simpler than a string of code that gets joined into a function. This separates the vertex push method for core attributes from the push methods for data-driven attributes. This will let us: - use a regular function for adding core attributes to buffers - and either: - generate a separate method for data driven attributes - or optimize adding attributes without code generation Data driven properties need to be evaluated once per feature, not once per vertex. This means we can evaluate them outside the main vertex loop and then add the same value for every vertex. The hot loop for lines, fills and symbols will be: for (var i = startIndex; i < endIndex; i++) { ... } If it's too slow in the current implementation we could add new StructArray methods for setting a single value for a range of elements. If we need to generate code it would be nice to contain that to StructArray. --- js/data/bucket.js | 72 +++++++++++++-------------------- js/data/bucket/circle_bucket.js | 11 ++++- js/render/draw_circle.js | 2 +- shaders/circle.vertex.glsl | 15 ++++++- test/js/data/bucket.test.js | 19 ++++++--- 5 files changed, 63 insertions(+), 56 deletions(-) diff --git a/js/data/bucket.js b/js/data/bucket.js index abc60d3ee73..473fcea190a 100644 --- a/js/data/bucket.js +++ b/js/data/bucket.js @@ -216,7 +216,7 @@ var AttributeType = { * @param {number} offset The offset of the attribute data in the currently bound GL buffer. * @param {Array} arguments to be passed to disabled attribute value functions */ -Bucket.prototype.setAttribPointers = function(programName, gl, program, offset, layer, args) { +Bucket.prototype.setAttribPointers = function(programName, gl, program, offset, layer, globalProperties) { var attribute; // Set disabled attributes @@ -226,7 +226,7 @@ Bucket.prototype.setAttribPointers = function(programName, gl, program, offset, if (layer.id !== attribute.layerId) continue; var attributeId = program[attribute.programName]; - gl['uniform' + attribute.components + 'fv'](attributeId, attribute.getValue.apply(this, args)); + gl['uniform' + attribute.components + 'fv'](attributeId, attribute.getValue(layer, globalProperties)); } // Set enabled attributes @@ -319,8 +319,6 @@ Bucket.prototype.createFilter = function() { } }; -Bucket.prototype._premultiplyColor = util.premultiply; - var FAKE_ZOOM_HISTORY = { lastIntegerZoom: Infinity, lastIntegerZoomTime: 0, lastZoom: 0 }; Bucket.prototype.recalculateStyleLayers = function() { for (var i = 0; i < this.childLayers.length; i++) { @@ -340,6 +338,27 @@ Bucket.prototype.getProgramMacros = function(programInterface, layer) { return macros; }; +Bucket.prototype.addPaintAttributes = function(interfaceName, globalProperties, featureProperties, startIndex, endIndex) { + var enabled = this.attributes[interfaceName].enabled; + var vertexArray = this.arrays[this.getBufferName(interfaceName, 'vertex')]; + for (var m = 0; m < enabled.length; m++) { + var attribute = enabled[m]; + + if (attribute.paintProperty === undefined) continue; + + var value = attribute.getValue(this.childLayers[attribute.layerIndex], globalProperties, featureProperties); + var multiplier = attribute.multiplier || 1; + + for (var i = startIndex; i < endIndex; i++) { + var vertex = vertexArray.get(i); + for (var c = 0; c < attribute.components; c++) { + var memberName = attribute.components > 1 ? (attribute.name + c) : attribute.name; + vertex[memberName] = value[c] * multiplier; + } + } + } +}; + var createVertexAddMethodCache = {}; function createVertexAddMethod(bucket, interfaceName) { var enabledAttributes = bucket.attributes[interfaceName].enabled; @@ -351,19 +370,10 @@ function createVertexAddMethod(bucket, interfaceName) { for (var i = 0; i < enabledAttributes.length; i++) { var attribute = enabledAttributes[i]; + if (attribute.paintProperty) continue; var attributePushArgs = []; - if (Array.isArray(attribute.value)) { - attributePushArgs = attributePushArgs.concat(attribute.value); - attributePushArgs[0] = '(layer = this.childLayers[' + attribute.layerIndex + '] ,' + attributePushArgs[0] + ')'; - } else { - body += 'layer = this.childLayers[' + attribute.layerIndex + '];\n'; - var attributeId = '_' + i; - body += 'var ' + attributeId + ' = ' + attribute.value + ';\n'; - for (var j = 0; j < attribute.components; j++) { - attributePushArgs.push(attributeId + '[' + j + ']'); - } - } + attributePushArgs = attributePushArgs.concat(attribute.value); var multipliedAttributePushArgs; if (attribute.multiplier) { @@ -404,35 +414,6 @@ function createElementBufferType(components) { }); } -var _getAttributeValueCache = {}; -function createGetAttributeValueMethod(bucket, interfaceName, attribute, layerIndex) { - if (!_getAttributeValueCache[interfaceName]) { - _getAttributeValueCache[interfaceName] = {}; - } - - if (!_getAttributeValueCache[interfaceName][attribute.name]) { - var bodyArgs = bucket.programInterfaces[interfaceName].attributeArgs; - var body = ''; - - body += 'var layer = this.childLayers[' + layerIndex + '];\n'; - - if (Array.isArray(attribute.value)) { - body += 'return [' + attribute.value.join(', ') + ']'; - } else { - body += 'return ' + attribute.value; - } - - if (attribute.multiplier) { - body += '.map(function(v) { return v * ' + attribute.multiplier + '; })'; - } - body += ';'; - - _getAttributeValueCache[interfaceName][attribute.name] = new Function(bodyArgs, body); - } - - return _getAttributeValueCache[interfaceName][attribute.name]; -} - function capitalize(string) { return string.charAt(0).toUpperCase() + string.slice(1); } @@ -449,7 +430,7 @@ function createAttributes(bucket) { if (!attribute.paintProperty && layer.id !== bucket.layer.id) continue; if (attribute.paintProperty && layer.isPaintValueFeatureConstant(attribute.paintProperty)) { interfaceAttributes.disabled.push(util.extend({}, attribute, { - getValue: createGetAttributeValueMethod(bucket, interfaceName, attribute, j), + getValue: attribute.getValue, name: layer.id + '__' + attribute.name, programName: 'a_' + attribute.name, layerId: layer.id, @@ -458,6 +439,7 @@ function createAttributes(bucket) { })); } else { interfaceAttributes.enabled.push(util.extend({}, attribute, { + getValue: attribute.getValue, name: layer.id + '__' + attribute.name, programName: 'a_' + attribute.name, layerId: layer.id, diff --git a/js/data/bucket/circle_bucket.js b/js/data/bucket/circle_bucket.js index 28ccb920e0c..e668d7eac38 100644 --- a/js/data/bucket/circle_bucket.js +++ b/js/data/bucket/circle_bucket.js @@ -39,7 +39,9 @@ CircleBucket.prototype.programInterfaces = { name: 'color', components: 4, type: 'Uint8', - value: 'this._premultiplyColor(layer.getPaintValue("circle-color", globalProperties, featureProperties))', + getValue: function(layer, globalProperties, featureProperties) { + return util.premultiply(layer.getPaintValue("circle-color", globalProperties, featureProperties)); + }, multiplier: 255, paintProperty: 'circle-color' }, { @@ -47,7 +49,9 @@ CircleBucket.prototype.programInterfaces = { components: 1, type: 'Uint16', isLayerConstant: false, - value: ['layer.getPaintValue("circle-radius", globalProperties, featureProperties)'], + getValue: function(layer, globalProperties, featureProperties) { + return [layer.getPaintValue("circle-radius", globalProperties, featureProperties)]; + }, multiplier: 10, paintProperty: 'circle-radius' }] @@ -58,6 +62,8 @@ CircleBucket.prototype.addFeature = function(feature) { var globalProperties = {zoom: this.zoom}; var geometries = loadGeometry(feature); + var startIndex = this.arrays.circleVertex.length; + for (var j = 0; j < geometries.length; j++) { for (var k = 0; k < geometries[j].length; k++) { @@ -90,4 +96,5 @@ CircleBucket.prototype.addFeature = function(feature) { } } + this.addPaintAttributes('circle', globalProperties, feature.properties, startIndex, this.arrays.circleVertex.length); }; diff --git a/js/render/draw_circle.js b/js/render/draw_circle.js index cfed8df0dc7..0dc846e1502 100644 --- a/js/render/draw_circle.js +++ b/js/render/draw_circle.js @@ -43,7 +43,7 @@ function drawCircles(painter, source, layer, coords) { for (var k = 0; k < elementGroups.length; k++) { var group = elementGroups[k]; var count = group.elementLength * 3; - bucket.setAttribPointers('circle', gl, program, group.vertexOffset, layer, [{$zoom: painter.transform.zoom}]); + bucket.setAttribPointers('circle', gl, program, group.vertexOffset, layer, {zoom: painter.transform.zoom}); gl.drawElements(gl.TRIANGLES, count, gl.UNSIGNED_SHORT, group.elementOffset); } } diff --git a/shaders/circle.vertex.glsl b/shaders/circle.vertex.glsl index a0457fc293a..99b2c885f44 100644 --- a/shaders/circle.vertex.glsl +++ b/shaders/circle.vertex.glsl @@ -23,10 +23,17 @@ varying lowp vec4 v_color; varying lowp float v_antialiasblur; void main(void) { + +#ifdef ATTRIBUTE_RADIUS + mediump float radius = a_radius / 10.0; +#else + mediump float radius = a_radius; +#endif + // unencode the extrusion vector that we snuck into the a_pos vector v_extrude = vec2(mod(a_pos, 2.0) * 2.0 - 1.0); - vec4 extrude = u_exmatrix * vec4(v_extrude * a_radius / 10.0, 0, 0); + vec4 extrude = u_exmatrix * vec4(v_extrude * radius, 0, 0); // multiply a_pos by 0.5, since we had it * 2 in order to sneak // in extrusion data gl_Position = u_matrix * vec4(floor(a_pos * 0.5), 0, 1); @@ -35,10 +42,14 @@ void main(void) { // Multiply the extrude by it so that it isn't affected by it. gl_Position += extrude * gl_Position.w; +#ifdef ATTRIBUTE_COLOR v_color = a_color / 255.0; +#else + v_color = a_color; +#endif // This is a minimum blur distance that serves as a faux-antialiasing for // the circle. since blur is a ratio of the circle's size and the intent is // to keep the blur at roughly 1px, the two are inversely related. - v_antialiasblur = 1.0 / u_devicepixelratio / (a_radius / 10.0); + v_antialiasblur = 1.0 / u_devicepixelratio / radius; } diff --git a/test/js/data/bucket.test.js b/test/js/data/bucket.test.js index b59170d26d5..b9e4a4cf9a3 100644 --- a/test/js/data/bucket.test.js +++ b/test/js/data/bucket.test.js @@ -24,15 +24,17 @@ test('Bucket', function(t) { attributeArgs: options.attributeArgs || ['x', 'y'], attributes: options.attributes || [{ - name: 'map', - type: 'Int16', - value: ['x'], - paintProperty: 'circle-color' - }, { name: 'box', components: 2, type: 'Int16', value: ['x * 2', 'y * 2'] + }, { + name: 'map', + type: 'Int16', + getValue: function(layer, globalProperties, featureProperties) { + return [featureProperties.x]; + }, + paintProperty: 'circle-color' }] } }; @@ -40,9 +42,11 @@ test('Bucket', function(t) { Class.prototype.addFeature = function(feature) { this.makeRoomFor('test', 1); var point = feature.loadGeometry()[0][0]; + var startIndex = this.arrays.testVertex.length; this.addTestVertex(point.x, point.y); this.addTestElement(1, 2, 3); this.addTestSecondElement(point.x, point.y); + this.addPaintAttributes('test', {}, feature.properties, startIndex, this.arrays.testVertex.length, this.debug); }; return Class; @@ -52,6 +56,9 @@ test('Bucket', function(t) { return { loadGeometry: function() { return [[{x: x, y: y}]]; + }, + properties: { + x: x } }; } @@ -140,7 +147,7 @@ test('Bucket', function(t) { attributes: [{ name: 'map', type: 'Int16', - value: ['5'], + getValue: function() { return [5]; }, paintProperty: 'circle-color' }], layers: [ From bba8b99d85fb7cf6ffe0c18e739b7e15b9647acd Mon Sep 17 00:00:00 2001 From: Ansis Brammanis Date: Mon, 11 Apr 2016 16:49:23 -0700 Subject: [PATCH 29/32] define vertex add methods as regular functions Now that paint attributes are added separately, we can switch to using regular functions instead of code strings that are joined into a method. This removes more code generation and it reduces the responsibility of the `Bucket` parent class. --- js/data/bucket.js | 45 ----------------- js/data/bucket/circle_bucket.js | 22 ++++----- js/data/bucket/fill_bucket.js | 9 ++-- js/data/bucket/line_bucket.js | 41 ++++++++-------- js/data/bucket/symbol_bucket.js | 85 +++++++++++++++++---------------- test/js/data/bucket.test.js | 14 +++--- 6 files changed, 86 insertions(+), 130 deletions(-) diff --git a/js/data/bucket.js b/js/data/bucket.js index 473fcea190a..1624e438a5c 100644 --- a/js/data/bucket.js +++ b/js/data/bucket.js @@ -47,9 +47,6 @@ Bucket.EXTENT = 8192; * style spec layer type. Because `Bucket` is an abstract class, * instances should be created via the `Bucket.create` method. * - * For performance reasons, `Bucket` creates its "add"s methods at - * runtime using `new Function(...)`. - * * @class Bucket * @private * @param options @@ -150,7 +147,6 @@ Bucket.prototype.createArrays = function() { var programInterface = this.programInterfaces[programName]; if (programInterface.vertexBuffer) { - var vertexAddMethodName = this.getAddMethodName(programName, 'vertex'); var vertexBufferName = this.getBufferName(programName, 'vertex'); var VertexArrayType = new StructArrayType({ @@ -160,8 +156,6 @@ Bucket.prototype.createArrays = function() { arrays[vertexBufferName] = new VertexArrayType(); arrayTypes[vertexBufferName] = VertexArrayType.serialize(); - - this[vertexAddMethodName] = this[vertexAddMethodName] || createVertexAddMethod(this, programName); } if (programInterface.elementBuffer) { @@ -359,45 +353,6 @@ Bucket.prototype.addPaintAttributes = function(interfaceName, globalProperties, } }; -var createVertexAddMethodCache = {}; -function createVertexAddMethod(bucket, interfaceName) { - var enabledAttributes = bucket.attributes[interfaceName].enabled; - var programInterface = bucket.programInterfaces[interfaceName]; - - var body = 'var layer;\n'; - - var pushArgs = []; - - for (var i = 0; i < enabledAttributes.length; i++) { - var attribute = enabledAttributes[i]; - if (attribute.paintProperty) continue; - - var attributePushArgs = []; - attributePushArgs = attributePushArgs.concat(attribute.value); - - var multipliedAttributePushArgs; - if (attribute.multiplier) { - multipliedAttributePushArgs = []; - for (var k = 0; k < attributePushArgs.length; k++) { - multipliedAttributePushArgs[k] = attributePushArgs[k] + '*' + attribute.multiplier; - } - } else { - multipliedAttributePushArgs = attributePushArgs; - } - - pushArgs = pushArgs.concat(multipliedAttributePushArgs); - } - - var bufferName = bucket.getBufferName(interfaceName, 'vertex'); - body += 'return this.arrays.' + bufferName + '.emplaceBack(' + pushArgs.join(',') + ');'; - - if (!createVertexAddMethodCache[body]) { - createVertexAddMethodCache[body] = new Function(programInterface.attributeArgs, body); - } - - return createVertexAddMethodCache[body]; -} - function createElementAddMethod(buffer) { return function(one, two, three) { return buffer.emplaceBack(one, two, three); diff --git a/js/data/bucket/circle_bucket.js b/js/data/bucket/circle_bucket.js index e668d7eac38..afdfa8c3eec 100644 --- a/js/data/bucket/circle_bucket.js +++ b/js/data/bucket/circle_bucket.js @@ -20,21 +20,21 @@ function CircleBucket() { CircleBucket.prototype = util.inherit(Bucket, {}); +CircleBucket.prototype.addCircleVertex = function(x, y, extrudeX, extrudeY) { + return this.arrays.circleVertex.emplaceBack( + (x * 2) + ((extrudeX + 1) / 2), + (y * 2) + ((extrudeY + 1) / 2)); +}; + CircleBucket.prototype.programInterfaces = { circle: { vertexBuffer: true, elementBuffer: true, - attributeArgs: ['globalProperties', 'featureProperties', 'x', 'y', 'extrudeX', 'extrudeY'], - attributes: [{ name: 'pos', components: 2, - type: 'Int16', - value: [ - '(x * 2) + ((extrudeX + 1) / 2)', - '(y * 2) + ((extrudeY + 1) / 2)' - ] + type: 'Int16' }, { name: 'color', components: 4, @@ -84,10 +84,10 @@ CircleBucket.prototype.addFeature = function(feature) { var group = this.makeRoomFor('circle', 4); - var index = this.addCircleVertex(globalProperties, feature.properties, x, y, -1, -1) - group.vertexStartIndex; - this.addCircleVertex(globalProperties, feature.properties, x, y, 1, -1); - this.addCircleVertex(globalProperties, feature.properties, x, y, 1, 1); - this.addCircleVertex(globalProperties, feature.properties, x, y, -1, 1); + var index = this.addCircleVertex(x, y, -1, -1) - group.vertexStartIndex; + this.addCircleVertex(x, y, 1, -1); + this.addCircleVertex(x, y, 1, 1); + this.addCircleVertex(x, y, -1, 1); group.vertexLength += 4; this.addCircleElement(index, index + 1, index + 2); diff --git a/js/data/bucket/fill_bucket.js b/js/data/bucket/fill_bucket.js index 9434fcb016c..b386feb8ac2 100644 --- a/js/data/bucket/fill_bucket.js +++ b/js/data/bucket/fill_bucket.js @@ -12,6 +12,10 @@ function FillBucket() { FillBucket.prototype = util.inherit(Bucket, {}); +FillBucket.prototype.addFillVertex = function(x, y) { + return this.arrays.fillVertex.emplaceBack(x, y); +}; + FillBucket.prototype.programInterfaces = { fill: { vertexBuffer: true, @@ -19,13 +23,10 @@ FillBucket.prototype.programInterfaces = { secondElementBuffer: true, secondElementBufferComponents: 2, - attributeArgs: ['x', 'y'], - attributes: [{ name: 'pos', components: 2, - type: 'Int16', - value: ['x', 'y'] + type: 'Int16' }] } }; diff --git a/js/data/bucket/line_bucket.js b/js/data/bucket/line_bucket.js index cb5400574c2..74642c8d50b 100644 --- a/js/data/bucket/line_bucket.js +++ b/js/data/bucket/line_bucket.js @@ -50,38 +50,37 @@ function LineBucket() { LineBucket.prototype = util.inherit(Bucket, {}); +LineBucket.prototype.addLineVertex = function(point, extrude, tx, ty, dir, linesofar) { + return this.arrays.lineVertex.emplaceBack( + // a_pos + (point.x << 1) | tx, + (point.y << 1) | ty, + // a_data + // add 128 to store an byte in an unsigned byte + Math.round(EXTRUDE_SCALE * extrude.x) + 128, + Math.round(EXTRUDE_SCALE * extrude.y) + 128, + // Encode the -1/0/1 direction value into the first two bits of .z of a_data. + // Combine it with the lower 6 bits of `linesofar` (shifted by 2 bites to make + // room for the direction value). The upper 8 bits of `linesofar` are placed in + // the `w` component. `linesofar` is scaled down by `LINE_DISTANCE_SCALE` so that + // we can store longer distances while sacrificing precision. + ((dir === 0 ? 0 : (dir < 0 ? -1 : 1)) + 1) | (((linesofar * LINE_DISTANCE_SCALE) & 0x3F) << 2), + (linesofar * LINE_DISTANCE_SCALE) >> 6); +}; + LineBucket.prototype.programInterfaces = { line: { vertexBuffer: true, elementBuffer: true, - attributeArgs: ['point', 'extrude', 'tx', 'ty', 'dir', 'linesofar'], - attributes: [{ name: 'pos', components: 2, - type: 'Int16', - value: [ - '(point.x << 1) | tx', - '(point.y << 1) | ty' - ] + type: 'Int16' }, { name: 'data', components: 4, - type: 'Uint8', - value: [ - // add 128 to store an byte in an unsigned byte - 'Math.round(' + EXTRUDE_SCALE + ' * extrude.x) + 128', - 'Math.round(' + EXTRUDE_SCALE + ' * extrude.y) + 128', - - // Encode the -1/0/1 direction value into the first two bits of .z of a_data. - // Combine it with the lower 6 bits of `linesofar` (shifted by 2 bites to make - // room for the direction value). The upper 8 bits of `linesofar` are placed in - // the `w` component. `linesofar` is scaled down by `LINE_DISTANCE_SCALE` so that - // we can store longer distances while sacrificing precision. - '((dir === 0 ? 0 : (dir < 0 ? -1 : 1)) + 1) | (((linesofar * ' + LINE_DISTANCE_SCALE + ') & 0x3F) << 2)', - '(linesofar * ' + LINE_DISTANCE_SCALE + ') >> 6' - ] + type: 'Uint8' }] } }; diff --git a/js/data/bucket/symbol_bucket.js b/js/data/bucket/symbol_bucket.js index 0ed102165cf..d725e84a104 100644 --- a/js/data/bucket/symbol_bucket.js +++ b/js/data/bucket/symbol_bucket.js @@ -33,83 +33,84 @@ function SymbolBucket(options) { SymbolBucket.prototype = util.inherit(Bucket, {}); -var programAttributeArgs = ['x', 'y', 'ox', 'oy', 'tx', 'ty', 'minzoom', 'maxzoom', 'labelminzoom']; - var programAttributes = [{ name: 'pos', components: 2, - type: 'Int16', - value: ['x', 'y'] + type: 'Int16' }, { name: 'offset', components: 2, - type: 'Int16', - value: [ - 'Math.round(ox * 64)', // use 1/64 pixels for placement - 'Math.round(oy * 64)' - ] + type: 'Int16' }, { name: 'data1', components: 4, - type: 'Uint8', - value: [ - 'tx / 4', // tex - 'ty / 4', // tex - '(labelminzoom || 0) * 10', // labelminzoom - '0' - ] + type: 'Uint8' }, { name: 'data2', components: 2, - type: 'Uint8', - value: [ - '(minzoom || 0) * 10', // minzoom - 'Math.min(maxzoom || 25, 25) * 10' // minzoom - ] + type: 'Uint8' }]; +function addVertex(array, x, y, ox, oy, tx, ty, minzoom, maxzoom, labelminzoom) { + return array.emplaceBack( + // pos + x, + y, + // offset + Math.round(ox * 64), // use 1/64 pixels for placement + Math.round(oy * 64), + // data1 + tx / 4, // tex + ty / 4, // tex + (labelminzoom || 0) * 10, // labelminzoom + 0, + // data2 + (minzoom || 0) * 10, // minzoom + Math.min(maxzoom || 25, 25) * 10); // minzoom +} + +SymbolBucket.prototype.addCollisionBoxVertex = function(point, extrude, maxZoom, placementZoom) { + return this.arrays.collisionBoxVertex.emplaceBack( + // pos + point.x, + point.y, + // extrude + Math.round(extrude.x), + Math.round(extrude.y), + // data + maxZoom * 10, + placementZoom * 10); +}; + SymbolBucket.prototype.programInterfaces = { glyph: { vertexBuffer: true, elementBuffer: true, - attributeArgs: programAttributeArgs, attributes: programAttributes }, icon: { vertexBuffer: true, elementBuffer: true, - attributeArgs: programAttributeArgs, attributes: programAttributes }, collisionBox: { vertexBuffer: true, - attributeArgs: ['point', 'extrude', 'maxZoom', 'placementZoom'], - attributes: [{ name: 'pos', components: 2, - type: 'Int16', - value: [ 'point.x', 'point.y' ] + type: 'Int16' }, { name: 'extrude', components: 2, - type: 'Int16', - value: [ - 'Math.round(extrude.x)', - 'Math.round(extrude.y)' - ] + type: 'Int16' }, { name: 'data', components: 2, - type: 'Uint8', - value: [ - 'maxZoom * 10', - 'placementZoom * 10' - ] + type: 'Uint8' }] } }; @@ -437,7 +438,7 @@ SymbolBucket.prototype.addSymbols = function(programName, quads, scale, keepUpri // TODO manual curry var addElement = this[this.getAddMethodName(programName, 'element')].bind(this); - var addVertex = this[this.getAddMethodName(programName, 'vertex')].bind(this); + var vertexArray = this.arrays[this.getBufferName(programName, 'vertex')]; var zoom = this.zoom; var placementZoom = Math.max(Math.log(scale) / Math.LN2 + zoom, 0); @@ -466,10 +467,10 @@ SymbolBucket.prototype.addSymbols = function(programName, quads, scale, keepUpri // Lower min zoom so that while fading out the label it can be shown outside of collision-free zoom levels if (minZoom === placementZoom) minZoom = 0; - var index = addVertex(anchorPoint.x, anchorPoint.y, tl.x, tl.y, tex.x, tex.y, minZoom, maxZoom, placementZoom) - group.vertexStartIndex; - addVertex(anchorPoint.x, anchorPoint.y, tr.x, tr.y, tex.x + tex.w, tex.y, minZoom, maxZoom, placementZoom); - addVertex(anchorPoint.x, anchorPoint.y, bl.x, bl.y, tex.x, tex.y + tex.h, minZoom, maxZoom, placementZoom); - addVertex(anchorPoint.x, anchorPoint.y, br.x, br.y, tex.x + tex.w, tex.y + tex.h, minZoom, maxZoom, placementZoom); + var index = addVertex(vertexArray, anchorPoint.x, anchorPoint.y, tl.x, tl.y, tex.x, tex.y, minZoom, maxZoom, placementZoom) - group.vertexStartIndex; + addVertex(vertexArray, anchorPoint.x, anchorPoint.y, tr.x, tr.y, tex.x + tex.w, tex.y, minZoom, maxZoom, placementZoom); + addVertex(vertexArray, anchorPoint.x, anchorPoint.y, bl.x, bl.y, tex.x, tex.y + tex.h, minZoom, maxZoom, placementZoom); + addVertex(vertexArray, anchorPoint.x, anchorPoint.y, br.x, br.y, tex.x + tex.w, tex.y + tex.h, minZoom, maxZoom, placementZoom); group.vertexLength += 4; addElement(index, index + 1, index + 2); diff --git a/test/js/data/bucket.test.js b/test/js/data/bucket.test.js index b9e4a4cf9a3..0dfb318f5f8 100644 --- a/test/js/data/bucket.test.js +++ b/test/js/data/bucket.test.js @@ -21,13 +21,10 @@ test('Bucket', function(t) { secondElementBuffer: 'testSecondElement', secondElementBufferComponents: 2, - attributeArgs: options.attributeArgs || ['x', 'y'], - attributes: options.attributes || [{ name: 'box', components: 2, - type: 'Int16', - value: ['x * 2', 'y * 2'] + type: 'Int16' }, { name: 'map', type: 'Int16', @@ -39,6 +36,10 @@ test('Bucket', function(t) { } }; + Class.prototype.addTestVertex = function(x, y) { + return this.arrays.testVertex.emplaceBack(x * 2, y * 2); + }; + Class.prototype.addFeature = function(feature) { this.makeRoomFor('test', 1); var point = feature.loadGeometry()[0][0]; @@ -171,8 +172,7 @@ test('Bucket', function(t) { var bucket = create({ attributes: [{ name: 'map', - type: 'Int16', - value: '[17]' + type: 'Int16' }] }); @@ -180,7 +180,7 @@ test('Bucket', function(t) { bucket.populateBuffers(); var v0 = bucket.arrays.testVertex.get(0); - t.equal(v0.layerid__map, 17); + t.equal(v0.layerid__map, 34); t.end(); }); From 816bf2977924dc376a68a2d72c6931fd5a322b95 Mon Sep 17 00:00:00 2001 From: Ansis Brammanis Date: Mon, 11 Apr 2016 23:19:40 -0700 Subject: [PATCH 30/32] remove elementAddMethod abstraction It's simpler to just add directly to the arrays. --- js/data/bucket.js | 18 ------------------ js/data/bucket/circle_bucket.js | 4 ++-- js/data/bucket/fill_bucket.js | 4 ++-- js/data/bucket/line_bucket.js | 6 +++--- js/data/bucket/symbol_bucket.js | 6 +++--- test/js/data/bucket.test.js | 4 ++-- 6 files changed, 12 insertions(+), 30 deletions(-) diff --git a/js/data/bucket.js b/js/data/bucket.js index 1624e438a5c..e03485b2da0 100644 --- a/js/data/bucket.js +++ b/js/data/bucket.js @@ -163,7 +163,6 @@ Bucket.prototype.createArrays = function() { var ElementArrayType = createElementBufferType(programInterface.elementBufferComponents); arrays[elementBufferName] = new ElementArrayType(); arrayTypes[elementBufferName] = ElementArrayType.serialize(); - this[this.getAddMethodName(programName, 'element')] = createElementAddMethod(this.arrays[elementBufferName]); } if (programInterface.secondElementBuffer) { @@ -171,7 +170,6 @@ Bucket.prototype.createArrays = function() { var SecondElementArrayType = createElementBufferType(programInterface.secondElementBufferComponents); arrays[secondElementBufferName] = new SecondElementArrayType(); arrayTypes[secondElementBufferName] = SecondElementArrayType.serialize(); - this[this.getAddMethodName(programName, 'secondElement')] = createElementAddMethod(this.arrays[secondElementBufferName]); } elementGroups[programName] = []; @@ -272,16 +270,6 @@ Bucket.prototype.bindBuffers = function(programInterfaceName, gl, options) { } }; -/** - * Get the name of the method used to add an item to a buffer. - * @param {string} programName The name of the program that will use the buffer - * @param {string} type One of "vertex", "element", or "secondElement" - * @returns {string} - */ -Bucket.prototype.getAddMethodName = function(programName, type) { - return 'add' + capitalize(programName) + capitalize(type); -}; - /** * Get the name of a buffer. * @param {string} programName The name of the program that will use the buffer @@ -353,12 +341,6 @@ Bucket.prototype.addPaintAttributes = function(interfaceName, globalProperties, } }; -function createElementAddMethod(buffer) { - return function(one, two, three) { - return buffer.emplaceBack(one, two, three); - }; -} - function createElementBufferType(components) { return new StructArrayType({ members: [{ diff --git a/js/data/bucket/circle_bucket.js b/js/data/bucket/circle_bucket.js index afdfa8c3eec..510c50771c7 100644 --- a/js/data/bucket/circle_bucket.js +++ b/js/data/bucket/circle_bucket.js @@ -90,8 +90,8 @@ CircleBucket.prototype.addFeature = function(feature) { this.addCircleVertex(x, y, -1, 1); group.vertexLength += 4; - this.addCircleElement(index, index + 1, index + 2); - this.addCircleElement(index, index + 3, index + 2); + this.arrays.circleElement.emplaceBack(index, index + 1, index + 2); + this.arrays.circleElement.emplaceBack(index, index + 3, index + 2); group.elementLength += 2; } } diff --git a/js/data/bucket/fill_bucket.js b/js/data/bucket/fill_bucket.js index b386feb8ac2..a479556834a 100644 --- a/js/data/bucket/fill_bucket.js +++ b/js/data/bucket/fill_bucket.js @@ -67,12 +67,12 @@ FillBucket.prototype.addFill = function(vertices) { // Only add triangles that have distinct vertices. if (i >= 2 && (currentVertex.x !== vertices[0].x || currentVertex.y !== vertices[0].y)) { - this.addFillElement(firstIndex, prevIndex, currentIndex); + this.arrays.fillElement.emplaceBack(firstIndex, prevIndex, currentIndex); group.elementLength++; } if (i >= 1) { - this.addFillSecondElement(prevIndex, currentIndex); + this.arrays.fillSecondElement.emplaceBack(prevIndex, currentIndex); group.secondElementLength++; } diff --git a/js/data/bucket/line_bucket.js b/js/data/bucket/line_bucket.js index 74642c8d50b..1dc7ef67a57 100644 --- a/js/data/bucket/line_bucket.js +++ b/js/data/bucket/line_bucket.js @@ -375,7 +375,7 @@ LineBucket.prototype.addCurrentVertex = function(currentVertex, distance, normal if (endLeft) extrude._sub(normal.perp()._mult(endLeft)); this.e3 = this.addLineVertex(currentVertex, extrude, tx, 0, endLeft, distance) - group.vertexStartIndex; if (this.e1 >= 0 && this.e2 >= 0) { - this.addLineElement(this.e1, this.e2, this.e3); + this.arrays.lineElement.emplaceBack(this.e1, this.e2, this.e3); group.elementLength++; } this.e1 = this.e2; @@ -385,7 +385,7 @@ LineBucket.prototype.addCurrentVertex = function(currentVertex, distance, normal if (endRight) extrude._sub(normal.perp()._mult(endRight)); this.e3 = this.addLineVertex(currentVertex, extrude, tx, 1, -endRight, distance) - group.vertexStartIndex; if (this.e1 >= 0 && this.e2 >= 0) { - this.addLineElement(this.e1, this.e2, this.e3); + this.arrays.lineElement.emplaceBack(this.e1, this.e2, this.e3); group.elementLength++; } this.e1 = this.e2; @@ -420,7 +420,7 @@ LineBucket.prototype.addPieSliceVertex = function(currentVertex, distance, extru group.vertexLength++; if (this.e1 >= 0 && this.e2 >= 0) { - this.addLineElement(this.e1, this.e2, this.e3); + this.arrays.lineElement.emplaceBack(this.e1, this.e2, this.e3); group.elementLength++; } diff --git a/js/data/bucket/symbol_bucket.js b/js/data/bucket/symbol_bucket.js index d725e84a104..ff111f588ff 100644 --- a/js/data/bucket/symbol_bucket.js +++ b/js/data/bucket/symbol_bucket.js @@ -437,7 +437,7 @@ SymbolBucket.prototype.addSymbols = function(programName, quads, scale, keepUpri var group = this.makeRoomFor(programName, 4 * quads.length); // TODO manual curry - var addElement = this[this.getAddMethodName(programName, 'element')].bind(this); + var elementArray = this.arrays[this.getBufferName(programName, 'element')]; var vertexArray = this.arrays[this.getBufferName(programName, 'vertex')]; var zoom = this.zoom; @@ -473,8 +473,8 @@ SymbolBucket.prototype.addSymbols = function(programName, quads, scale, keepUpri addVertex(vertexArray, anchorPoint.x, anchorPoint.y, br.x, br.y, tex.x + tex.w, tex.y + tex.h, minZoom, maxZoom, placementZoom); group.vertexLength += 4; - addElement(index, index + 1, index + 2); - addElement(index + 1, index + 2, index + 3); + elementArray.emplaceBack(index, index + 1, index + 2); + elementArray.emplaceBack(index + 1, index + 2, index + 3); group.elementLength += 2; } diff --git a/test/js/data/bucket.test.js b/test/js/data/bucket.test.js index 0dfb318f5f8..64d2a5e26a2 100644 --- a/test/js/data/bucket.test.js +++ b/test/js/data/bucket.test.js @@ -45,8 +45,8 @@ test('Bucket', function(t) { var point = feature.loadGeometry()[0][0]; var startIndex = this.arrays.testVertex.length; this.addTestVertex(point.x, point.y); - this.addTestElement(1, 2, 3); - this.addTestSecondElement(point.x, point.y); + this.arrays.testElement.emplaceBack(1, 2, 3); + this.arrays.testSecondElement.emplaceBack(point.x, point.y); this.addPaintAttributes('test', {}, feature.properties, startIndex, this.arrays.testVertex.length, this.debug); }; From 0b17310e9b9745abb83cba71cd69cd9667dd3d29 Mon Sep 17 00:00:00 2001 From: Ansis Brammanis Date: Mon, 11 Apr 2016 23:00:39 -0700 Subject: [PATCH 31/32] split paint attributes into a separate buffer - avoids the need to namespace attribute names with string concatenation - this let's us use attribute.name as the shader attribute name - this will let us use instanced rendering (#1898) with a shorter buffer when that extension is available. --- js/data/bucket.js | 145 +++++++++++++++++++------------- js/data/bucket/circle_bucket.js | 6 +- js/data/bucket/fill_bucket.js | 2 +- js/data/bucket/line_bucket.js | 4 +- js/data/bucket/symbol_bucket.js | 14 +-- js/render/draw_circle.js | 4 +- js/util/struct_array.js | 30 ++++--- shaders/circle.vertex.glsl | 8 +- test/js/data/bucket.test.js | 43 ++++++---- 9 files changed, 150 insertions(+), 106 deletions(-) diff --git a/js/data/bucket.js b/js/data/bucket.js index e03485b2da0..3b4a1d9f83d 100644 --- a/js/data/bucket.js +++ b/js/data/bucket.js @@ -78,7 +78,7 @@ function Bucket(options) { this.elementGroups = options.elementGroups; this.buffers = util.mapObject(options.arrays, function(array, bufferName) { var arrayType = options.arrayTypes[bufferName]; - var type = (arrayType.members[0].name === 'vertices' ? Buffer.BufferType.ELEMENT : Buffer.BufferType.VERTEX); + var type = (arrayType.members.length && arrayType.members[0].name === 'vertices' ? Buffer.BufferType.ELEMENT : Buffer.BufferType.VERTEX); return new Buffer(array, arrayType, type); }); } @@ -150,12 +150,25 @@ Bucket.prototype.createArrays = function() { var vertexBufferName = this.getBufferName(programName, 'vertex'); var VertexArrayType = new StructArrayType({ - members: this.attributes[programName].enabled, + members: this.attributes[programName].coreAttributes, alignment: Buffer.VERTEX_ATTRIBUTE_ALIGNMENT }); arrays[vertexBufferName] = new VertexArrayType(); arrayTypes[vertexBufferName] = VertexArrayType.serialize(); + + var layerPaintAttributes = this.attributes[programName].paintAttributes; + for (var layerName in layerPaintAttributes) { + var paintVertexBufferName = this.getBufferName(layerName, programName); + + var PaintVertexArrayType = new StructArrayType({ + members: layerPaintAttributes[layerName].enabled, + alignment: Buffer.VERTEX_ATTRIBUTE_ALIGNMENT + }); + + arrays[paintVertexBufferName] = new PaintVertexArrayType(); + arrayTypes[paintVertexBufferName] = PaintVertexArrayType.serialize(); + } } if (programInterface.elementBuffer) { @@ -208,30 +221,40 @@ var AttributeType = { * @param {number} offset The offset of the attribute data in the currently bound GL buffer. * @param {Array} arguments to be passed to disabled attribute value functions */ -Bucket.prototype.setAttribPointers = function(programName, gl, program, offset, layer, globalProperties) { +Bucket.prototype.setAttribPointers = function(programName, gl, program, offset, layer, globalProperties, bindPaint) { var attribute; - // Set disabled attributes - var disabledAttributes = this.attributes[programName].disabled; - for (var i = 0; i < disabledAttributes.length; i++) { - attribute = disabledAttributes[i]; - if (layer.id !== attribute.layerId) continue; - - var attributeId = program[attribute.programName]; - gl['uniform' + attribute.components + 'fv'](attributeId, attribute.getValue(layer, globalProperties)); + if (bindPaint) { + // Set disabled attributes + var disabledAttributes = this.attributes[programName].paintAttributes[layer.id].disabled; + for (var i = 0; i < disabledAttributes.length; i++) { + attribute = disabledAttributes[i]; + var attributeId = program[attribute.name]; + gl['uniform' + attribute.components + 'fv'](attributeId, attribute.getValue(layer, globalProperties)); + } } // Set enabled attributes - var enabledAttributes = this.attributes[programName].enabled; - var vertexBuffer = this.buffers[this.getBufferName(programName, 'vertex')]; + var enabledAttributes = bindPaint ? + this.attributes[programName].paintAttributes[layer.id].enabled : + this.attributes[programName].coreAttributes; + + var vertexBuffer = bindPaint ? + this.buffers[this.getBufferName(layer.id, programName)] : + this.buffers[this.getBufferName(programName, 'vertex')]; + + if (bindPaint) { + // TODO remove + var bytesPerElement = this.buffers[this.getBufferName(programName, 'vertex')].itemSize; + offset = offset / bytesPerElement * vertexBuffer.itemSize; + } for (var j = 0; j < enabledAttributes.length; j++) { attribute = enabledAttributes[j]; - if (attribute.paintProperty && layer.id !== attribute.layerId) continue; if (!getMember(attribute.name)) continue; gl.vertexAttribPointer( - program[attribute.programName], + program[attribute.name], attribute.components, gl[AttributeType[attribute.type]], false, @@ -270,6 +293,10 @@ Bucket.prototype.bindBuffers = function(programInterfaceName, gl, options) { } }; +Bucket.prototype.bindPaintBuffer = function(programInterfaceName, gl, layerID) { + this.buffers[this.getBufferName(layerID, programInterfaceName)].bind(gl); +}; + /** * Get the name of a buffer. * @param {string} programName The name of the program that will use the buffer @@ -289,6 +316,7 @@ Bucket.prototype.serialize = function() { return array.serialize(); }), arrayTypes: this.arrayTypes, + childLayerIds: this.childLayers.map(function(layer) { return layer.id; }) @@ -310,32 +338,35 @@ Bucket.prototype.recalculateStyleLayers = function() { Bucket.prototype.getProgramMacros = function(programInterface, layer) { var macros = []; - var enabledAttributes = this.attributes[programInterface].enabled; + var enabledAttributes = this.attributes[programInterface].paintAttributes[layer.id].enabled; for (var i = 0; i < enabledAttributes.length; i++) { - var enabledAttribute = enabledAttributes[i]; - if (enabledAttribute.paintProperty && enabledAttribute.layerId === layer.id) { - macros.push(enabledAttribute.macro); - } + macros.push('ATTRIBUTE_' + enabledAttributes[i].name.toUpperCase()); } return macros; }; Bucket.prototype.addPaintAttributes = function(interfaceName, globalProperties, featureProperties, startIndex, endIndex) { - var enabled = this.attributes[interfaceName].enabled; - var vertexArray = this.arrays[this.getBufferName(interfaceName, 'vertex')]; - for (var m = 0; m < enabled.length; m++) { - var attribute = enabled[m]; - - if (attribute.paintProperty === undefined) continue; - - var value = attribute.getValue(this.childLayers[attribute.layerIndex], globalProperties, featureProperties); - var multiplier = attribute.multiplier || 1; - - for (var i = startIndex; i < endIndex; i++) { - var vertex = vertexArray.get(i); - for (var c = 0; c < attribute.components; c++) { - var memberName = attribute.components > 1 ? (attribute.name + c) : attribute.name; - vertex[memberName] = value[c] * multiplier; + for (var l = 0; l < this.childLayers.length; l++) { + var layer = this.childLayers[l]; + var length = this.arrays[this.getBufferName(interfaceName, 'vertex')].length; + var vertexArray = this.arrays[this.getBufferName(layer.id, interfaceName)]; + var enabled = this.attributes[interfaceName].paintAttributes[layer.id].enabled; + for (var m = 0; m < enabled.length; m++) { + var attribute = enabled[m]; + + if (attribute.paintProperty === undefined) continue; + + var value = attribute.getValue(layer, globalProperties, featureProperties); + var multiplier = attribute.multiplier || 1; + var components = attribute.components || 1; + + vertexArray.resize(length); + for (var i = startIndex; i < endIndex; i++) { + var vertex = vertexArray.get(i); + for (var c = 0; c < components; c++) { + var memberName = components > 1 ? (attribute.name + c) : attribute.name; + vertex[memberName] = value[c] * multiplier; + } } } } @@ -358,32 +389,30 @@ function capitalize(string) { function createAttributes(bucket) { var attributes = {}; for (var interfaceName in bucket.programInterfaces) { - var interfaceAttributes = attributes[interfaceName] = { enabled: [], disabled: [] }; + var interfaceAttributes = attributes[interfaceName] = { coreAttributes: [], paintAttributes: {} }; + var layerPaintAttributes = interfaceAttributes.paintAttributes; + + for (var c = 0; c < bucket.childLayers.length; c++) { + var childLayer = bucket.childLayers[c]; + layerPaintAttributes[childLayer.id] = { enabled: [], disabled: [] }; + } + var interface_ = bucket.programInterfaces[interfaceName]; for (var i = 0; i < interface_.attributes.length; i++) { var attribute = interface_.attributes[i]; - for (var j = 0; j < bucket.childLayers.length; j++) { - var layer = bucket.childLayers[j]; - if (!attribute.paintProperty && layer.id !== bucket.layer.id) continue; - if (attribute.paintProperty && layer.isPaintValueFeatureConstant(attribute.paintProperty)) { - interfaceAttributes.disabled.push(util.extend({}, attribute, { - getValue: attribute.getValue, - name: layer.id + '__' + attribute.name, - programName: 'a_' + attribute.name, - layerId: layer.id, - layerIndex: j, - components: attribute.components || 1 - })); - } else { - interfaceAttributes.enabled.push(util.extend({}, attribute, { - getValue: attribute.getValue, - name: layer.id + '__' + attribute.name, - programName: 'a_' + attribute.name, - layerId: layer.id, - layerIndex: j, - macro: 'ATTRIBUTE_' + attribute.name.toUpperCase(), - components: attribute.components || 1 - })); + + if (attribute.paintProperty === undefined) { + interfaceAttributes.coreAttributes.push(attribute); + } else { + for (var j = 0; j < bucket.childLayers.length; j++) { + var layer = bucket.childLayers[j]; + var paintAttributes = layerPaintAttributes[layer.id]; + + if (layer.isPaintValueFeatureConstant(attribute.paintProperty)) { + paintAttributes.disabled.push(attribute); + } else { + paintAttributes.enabled.push(attribute); + } } } } diff --git a/js/data/bucket/circle_bucket.js b/js/data/bucket/circle_bucket.js index 510c50771c7..21d0b9952b9 100644 --- a/js/data/bucket/circle_bucket.js +++ b/js/data/bucket/circle_bucket.js @@ -32,11 +32,11 @@ CircleBucket.prototype.programInterfaces = { elementBuffer: true, attributes: [{ - name: 'pos', + name: 'a_pos', components: 2, type: 'Int16' }, { - name: 'color', + name: 'a_color', components: 4, type: 'Uint8', getValue: function(layer, globalProperties, featureProperties) { @@ -45,7 +45,7 @@ CircleBucket.prototype.programInterfaces = { multiplier: 255, paintProperty: 'circle-color' }, { - name: 'radius', + name: 'a_radius', components: 1, type: 'Uint16', isLayerConstant: false, diff --git a/js/data/bucket/fill_bucket.js b/js/data/bucket/fill_bucket.js index a479556834a..6b874562f1f 100644 --- a/js/data/bucket/fill_bucket.js +++ b/js/data/bucket/fill_bucket.js @@ -24,7 +24,7 @@ FillBucket.prototype.programInterfaces = { secondElementBufferComponents: 2, attributes: [{ - name: 'pos', + name: 'a_pos', components: 2, type: 'Int16' }] diff --git a/js/data/bucket/line_bucket.js b/js/data/bucket/line_bucket.js index 1dc7ef67a57..a87cfc48000 100644 --- a/js/data/bucket/line_bucket.js +++ b/js/data/bucket/line_bucket.js @@ -74,11 +74,11 @@ LineBucket.prototype.programInterfaces = { elementBuffer: true, attributes: [{ - name: 'pos', + name: 'a_pos', components: 2, type: 'Int16' }, { - name: 'data', + name: 'a_data', components: 4, type: 'Uint8' }] diff --git a/js/data/bucket/symbol_bucket.js b/js/data/bucket/symbol_bucket.js index ff111f588ff..7b424b34d22 100644 --- a/js/data/bucket/symbol_bucket.js +++ b/js/data/bucket/symbol_bucket.js @@ -34,19 +34,19 @@ function SymbolBucket(options) { SymbolBucket.prototype = util.inherit(Bucket, {}); var programAttributes = [{ - name: 'pos', + name: 'a_pos', components: 2, type: 'Int16' }, { - name: 'offset', + name: 'a_offset', components: 2, type: 'Int16' }, { - name: 'data1', + name: 'a_data1', components: 4, type: 'Uint8' }, { - name: 'data2', + name: 'a_data2', components: 2, type: 'Uint8' }]; @@ -100,15 +100,15 @@ SymbolBucket.prototype.programInterfaces = { vertexBuffer: true, attributes: [{ - name: 'pos', + name: 'a_pos', components: 2, type: 'Int16' }, { - name: 'extrude', + name: 'a_extrude', components: 2, type: 'Int16' }, { - name: 'data', + name: 'a_data', components: 2, type: 'Uint8' }] diff --git a/js/render/draw_circle.js b/js/render/draw_circle.js index 0dc846e1502..1ae1e15ddd0 100644 --- a/js/render/draw_circle.js +++ b/js/render/draw_circle.js @@ -39,11 +39,13 @@ function drawCircles(painter, source, layer, coords) { )); painter.setExMatrix(painter.transform.exMatrix); - bucket.bindBuffers('circle', gl); for (var k = 0; k < elementGroups.length; k++) { var group = elementGroups[k]; var count = group.elementLength * 3; + bucket.bindBuffers('circle', gl); bucket.setAttribPointers('circle', gl, program, group.vertexOffset, layer, {zoom: painter.transform.zoom}); + bucket.bindPaintBuffer('circle', gl, layer.id); + bucket.setAttribPointers('circle', gl, program, group.vertexOffset, layer, {zoom: painter.transform.zoom}, true); gl.drawElements(gl.TRIANGLES, count, gl.UNSIGNED_SHORT, group.elementOffset); } } diff --git a/js/util/struct_array.js b/js/util/struct_array.js index 8cfe39d3156..9568ecdea40 100644 --- a/js/util/struct_array.js +++ b/js/util/struct_array.js @@ -169,8 +169,7 @@ function createEmplaceBack(members, bytesPerElement) { var argNames = []; var body = '' + 'var i = this.length;\n' + - 'this.length++;\n' + - 'if (this.length > this.capacity) this._resize(this.length);\n'; + 'this.resize(this.length + 1);\n'; for (var m = 0; m < members.length; m++) { var member = members[m]; @@ -240,9 +239,8 @@ function StructArray(serialized) { // Create a new StructArray } else { - this.length = 0; - this.capacity = 0; - this._resize(this.DEFAULT_CAPACITY); + this.capacity = -1; + this.resize(0); } } @@ -294,17 +292,21 @@ StructArray.prototype.trim = function() { }; /** - * Resize the array so that it fits at least `n` elements. - * @private - * @param {number} n The number of elements that must fit in the array after the resize. + * Resize the array. + * If `n` is greater than the current length then additional elements with undefined values are added. + * If `n` is less than the current length then the array will be reduced to the first `n` elements. + * @param {number} n The new size of the array. */ -StructArray.prototype._resize = function(n) { - this.capacity = Math.max(n, Math.floor(this.capacity * this.RESIZE_MULTIPLIER)); - this.arrayBuffer = new ArrayBuffer(this.capacity * this.bytesPerElement); +StructArray.prototype.resize = function(n) { + this.length = n; + if (n > this.capacity) { + this.capacity = Math.max(n, Math.floor(this.capacity * this.RESIZE_MULTIPLIER), this.DEFAULT_CAPACITY); + this.arrayBuffer = new ArrayBuffer(this.capacity * this.bytesPerElement); - var oldUint8Array = this.uint8; - this._refreshViews(); - if (oldUint8Array) this.uint8.set(oldUint8Array); + var oldUint8Array = this.uint8; + this._refreshViews(); + if (oldUint8Array) this.uint8.set(oldUint8Array); + } }; /** diff --git a/shaders/circle.vertex.glsl b/shaders/circle.vertex.glsl index 99b2c885f44..f607f3a6940 100644 --- a/shaders/circle.vertex.glsl +++ b/shaders/circle.vertex.glsl @@ -6,13 +6,13 @@ uniform float u_devicepixelratio; attribute vec2 a_pos; -#ifdef ATTRIBUTE_COLOR +#ifdef ATTRIBUTE_A_COLOR attribute lowp vec4 a_color; #else uniform lowp vec4 a_color; #endif -#ifdef ATTRIBUTE_RADIUS +#ifdef ATTRIBUTE_A_RADIUS attribute mediump float a_radius; #else uniform mediump float a_radius; @@ -24,7 +24,7 @@ varying lowp float v_antialiasblur; void main(void) { -#ifdef ATTRIBUTE_RADIUS +#ifdef ATTRIBUTE_A_RADIUS mediump float radius = a_radius / 10.0; #else mediump float radius = a_radius; @@ -42,7 +42,7 @@ void main(void) { // Multiply the extrude by it so that it isn't affected by it. gl_Position += extrude * gl_Position.w; -#ifdef ATTRIBUTE_COLOR +#ifdef ATTRIBUTE_A_COLOR v_color = a_color / 255.0; #else v_color = a_color; diff --git a/test/js/data/bucket.test.js b/test/js/data/bucket.test.js index 64d2a5e26a2..6ac069d906f 100644 --- a/test/js/data/bucket.test.js +++ b/test/js/data/bucket.test.js @@ -47,7 +47,7 @@ test('Bucket', function(t) { this.addTestVertex(point.x, point.y); this.arrays.testElement.emplaceBack(1, 2, 3); this.arrays.testSecondElement.emplaceBack(point.x, point.y); - this.addPaintAttributes('test', {}, feature.properties, startIndex, this.arrays.testVertex.length, this.debug); + this.addPaintAttributes('test', {}, feature.properties, startIndex, this.arrays.testVertex.length); }; return Class; @@ -105,9 +105,12 @@ test('Bucket', function(t) { var testVertex = bucket.arrays.testVertex; t.equal(testVertex.length, 1); var v0 = testVertex.get(0); - t.equal(v0.layerid__map, 17); - t.equal(v0.layerid__box0, 34); - t.equal(v0.layerid__box1, 84); + t.equal(v0.box0, 34); + t.equal(v0.box1, 84); + var paintVertex = bucket.arrays.layeridTest; + t.equal(paintVertex.length, 1); + var p0 = paintVertex.get(0); + t.equal(p0.map, 17); var testElement = bucket.arrays.testElement; t.equal(testElement.length, 1); @@ -135,10 +138,12 @@ test('Bucket', function(t) { bucket.populateBuffers(); var v0 = bucket.arrays.testVertex.get(0); - t.equal(v0.one__map, 17); - t.equal(v0.two__map, 17); - t.equal(v0.one__box0, 34); - t.equal(v0.one__box1, 84); + var a0 = bucket.arrays.oneTest.get(0); + var b0 = bucket.arrays.twoTest.get(0); + t.equal(a0.map, 17); + t.equal(b0.map, 17); + t.equal(v0.box0, 34); + t.equal(v0.box1, 84); t.end(); }); @@ -161,7 +166,7 @@ test('Bucket', function(t) { t.equal(bucket.arrays.testVertex.bytesPerElement, 0); t.deepEqual( - bucket.attributes.test.disabled[0].getValue.call(bucket), + bucket.attributes.test.paintAttributes.one.disabled[0].getValue.call(bucket), [5] ); @@ -180,7 +185,7 @@ test('Bucket', function(t) { bucket.populateBuffers(); var v0 = bucket.arrays.testVertex.get(0); - t.equal(v0.layerid__map, 34); + t.equal(v0.map, 34); t.end(); }); @@ -214,9 +219,12 @@ test('Bucket', function(t) { var testVertex = bucket.arrays.testVertex; t.equal(testVertex.length, 1); var v0 = testVertex.get(0); - t.equal(v0.layerid__map, 17); - t.equal(v0.layerid__box0, 34); - t.equal(v0.layerid__box1, 84); + t.equal(v0.box0, 34); + t.equal(v0.box1, 84); + var testPaintVertex = bucket.arrays.layeridTest; + t.equal(testPaintVertex.length, 1); + var p0 = testPaintVertex.get(0); + t.equal(p0.map, 17); var testElement = bucket.arrays.testElement; t.equal(testElement.length, 1); @@ -249,9 +257,12 @@ test('Bucket', function(t) { var testVertex = bucket.arrays.testVertex; t.equal(testVertex.length, 1); var v0 = testVertex.get(0); - t.equal(v0.layerid__map, 17); - t.equal(v0.layerid__box0, 34); - t.equal(v0.layerid__box1, 84); + t.equal(v0.box0, 34); + t.equal(v0.box1, 84); + var testPaintVertex = bucket.arrays.layeridTest; + t.equal(testPaintVertex.length, 1); + var p0 = testPaintVertex.get(0); + t.equal(p0.map, 17); var testElement = bucket.arrays.testElement; t.equal(testElement.length, 1); From 31dc4ac9cf40f561a0567df38909a323a3bd9c05 Mon Sep 17 00:00:00 2001 From: Ansis Brammanis Date: Sat, 9 Apr 2016 23:12:28 -0700 Subject: [PATCH 32/32] move setVertexAttribPointers to Buffer since it no longer needs access to anything in Bucket Set attribute pointers using info from the array type. --- js/data/bucket.js | 86 +++++++------------------------ js/data/buffer.js | 38 ++++++++++++++ js/render/draw_circle.js | 8 +-- js/render/draw_collision_debug.js | 2 +- js/render/draw_fill.js | 4 +- js/render/draw_line.js | 2 +- js/render/draw_symbol.js | 4 +- 7 files changed, 66 insertions(+), 78 deletions(-) diff --git a/js/data/bucket.js b/js/data/bucket.js index 3b4a1d9f83d..1f3b8bea0e3 100644 --- a/js/data/bucket.js +++ b/js/data/bucket.js @@ -150,7 +150,7 @@ Bucket.prototype.createArrays = function() { var vertexBufferName = this.getBufferName(programName, 'vertex'); var VertexArrayType = new StructArrayType({ - members: this.attributes[programName].coreAttributes, + members: this.attributes[programName].layoutAttributes, alignment: Buffer.VERTEX_ATTRIBUTE_ALIGNMENT }); @@ -201,80 +201,28 @@ Bucket.prototype.trimArrays = function() { } }; -/** - * @enum {string} AttributeType - * @private - * @readonly - */ -var AttributeType = { - Int8: 'BYTE', - Uint8: 'UNSIGNED_BYTE', - Int16: 'SHORT', - Uint16: 'UNSIGNED_SHORT' -}; - /** * Set the attribute pointers in a WebGL context * @private * @param gl The WebGL context * @param program The active WebGL program * @param {number} offset The offset of the attribute data in the currently bound GL buffer. - * @param {Array} arguments to be passed to disabled attribute value functions */ -Bucket.prototype.setAttribPointers = function(programName, gl, program, offset, layer, globalProperties, bindPaint) { - var attribute; - - if (bindPaint) { - // Set disabled attributes - var disabledAttributes = this.attributes[programName].paintAttributes[layer.id].disabled; - for (var i = 0; i < disabledAttributes.length; i++) { - attribute = disabledAttributes[i]; - var attributeId = program[attribute.name]; - gl['uniform' + attribute.components + 'fv'](attributeId, attribute.getValue(layer, globalProperties)); - } - } - - // Set enabled attributes - var enabledAttributes = bindPaint ? - this.attributes[programName].paintAttributes[layer.id].enabled : - this.attributes[programName].coreAttributes; - - var vertexBuffer = bindPaint ? - this.buffers[this.getBufferName(layer.id, programName)] : - this.buffers[this.getBufferName(programName, 'vertex')]; - - if (bindPaint) { - // TODO remove - var bytesPerElement = this.buffers[this.getBufferName(programName, 'vertex')].itemSize; - offset = offset / bytesPerElement * vertexBuffer.itemSize; - } - - for (var j = 0; j < enabledAttributes.length; j++) { - attribute = enabledAttributes[j]; - if (!getMember(attribute.name)) continue; - - gl.vertexAttribPointer( - program[attribute.name], - attribute.components, - gl[AttributeType[attribute.type]], - false, - vertexBuffer.arrayType.bytesPerElement, - offset + getMember(attribute.name).offset - ); - } +Bucket.prototype.setAttribPointers = function(programName, gl, program, offset) { + var vertexBuffer = this.buffers[this.getBufferName(programName, 'vertex')]; + vertexBuffer.setVertexAttribPointers(gl, program, offset / vertexBuffer.itemSize); +}; - function getMember(memberName) { - var members = vertexBuffer.arrayType.members; - for (var k = 0; k < members.length; k++) { - var member = members[k]; - if (member.name === memberName) { - return member; - } - } +Bucket.prototype.setUniforms = function(gl, programName, program, layer, globalProperties) { + var disabledAttributes = this.attributes[programName].paintAttributes[layer.id].disabled; + for (var i = 0; i < disabledAttributes.length; i++) { + var attribute = disabledAttributes[i]; + var attributeId = program[attribute.name]; + gl['uniform' + attribute.components + 'fv'](attributeId, attribute.getValue(layer, globalProperties)); } }; -Bucket.prototype.bindBuffers = function(programInterfaceName, gl, options) { +Bucket.prototype.bindLayoutBuffers = function(programInterfaceName, gl, options) { var programInterface = this.programInterfaces[programInterfaceName]; if (programInterface.vertexBuffer) { @@ -293,8 +241,10 @@ Bucket.prototype.bindBuffers = function(programInterfaceName, gl, options) { } }; -Bucket.prototype.bindPaintBuffer = function(programInterfaceName, gl, layerID) { - this.buffers[this.getBufferName(layerID, programInterfaceName)].bind(gl); +Bucket.prototype.bindPaintBuffer = function(gl, interfaceName, layerID, program, vertexStartIndex) { + var buffer = this.buffers[this.getBufferName(layerID, interfaceName)]; + buffer.bind(gl); + buffer.setVertexAttribPointers(gl, program, vertexStartIndex); }; /** @@ -389,7 +339,7 @@ function capitalize(string) { function createAttributes(bucket) { var attributes = {}; for (var interfaceName in bucket.programInterfaces) { - var interfaceAttributes = attributes[interfaceName] = { coreAttributes: [], paintAttributes: {} }; + var interfaceAttributes = attributes[interfaceName] = { layoutAttributes: [], paintAttributes: {} }; var layerPaintAttributes = interfaceAttributes.paintAttributes; for (var c = 0; c < bucket.childLayers.length; c++) { @@ -402,7 +352,7 @@ function createAttributes(bucket) { var attribute = interface_.attributes[i]; if (attribute.paintProperty === undefined) { - interfaceAttributes.coreAttributes.push(attribute); + interfaceAttributes.layoutAttributes.push(attribute); } else { for (var j = 0; j < bucket.childLayers.length; j++) { var layer = bucket.childLayers[j]; diff --git a/js/data/buffer.js b/js/data/buffer.js index 53092813981..6137af8edea 100644 --- a/js/data/buffer.js +++ b/js/data/buffer.js @@ -1,5 +1,7 @@ 'use strict'; +var assert = require('assert'); + module.exports = Buffer; /** @@ -41,6 +43,42 @@ Buffer.prototype.bind = function(gl) { } }; +/** + * @enum {string} AttributeType + * @private + * @readonly + */ +var AttributeType = { + Int8: 'BYTE', + Uint8: 'UNSIGNED_BYTE', + Int16: 'SHORT', + Uint16: 'UNSIGNED_SHORT' +}; + +/** + * Set the attribute pointers in a WebGL context + * @private + * @param gl The WebGL context + * @param program The active WebGL program + * @param {number} offset The index offset of the attribute data in the currently bound GL buffer. + */ +Buffer.prototype.setVertexAttribPointers = function(gl, program, offset) { + for (var j = 0; j < this.attributes.length; j++) { + var member = this.attributes[j]; + var attribIndex = program[member.name]; + assert(attribIndex !== undefined, 'array member "' + member.name + '" name does not match shader attribute name'); + + gl.vertexAttribPointer( + attribIndex, + member.components, + gl[AttributeType[member.type]], + false, + this.arrayType.bytesPerElement, + offset * this.arrayType.bytesPerElement + member.offset + ); + } +}; + /** * Destroy the GL buffer bound to the given WebGL context * @private diff --git a/js/render/draw_circle.js b/js/render/draw_circle.js index 1ae1e15ddd0..babaa712f88 100644 --- a/js/render/draw_circle.js +++ b/js/render/draw_circle.js @@ -42,10 +42,10 @@ function drawCircles(painter, source, layer, coords) { for (var k = 0; k < elementGroups.length; k++) { var group = elementGroups[k]; var count = group.elementLength * 3; - bucket.bindBuffers('circle', gl); - bucket.setAttribPointers('circle', gl, program, group.vertexOffset, layer, {zoom: painter.transform.zoom}); - bucket.bindPaintBuffer('circle', gl, layer.id); - bucket.setAttribPointers('circle', gl, program, group.vertexOffset, layer, {zoom: painter.transform.zoom}, true); + bucket.bindLayoutBuffers('circle', gl); + bucket.setAttribPointers('circle', gl, program, group.vertexOffset); + bucket.bindPaintBuffer(gl, 'circle', layer.id, program, group.vertexStartIndex); + bucket.setUniforms(gl, 'circle', program, layer, {zoom: painter.transform.zoom}); gl.drawElements(gl.TRIANGLES, count, gl.UNSIGNED_SHORT, group.elementOffset); } } diff --git a/js/render/draw_collision_debug.js b/js/render/draw_collision_debug.js index c6257f4563b..4c6e2cc62fb 100644 --- a/js/render/draw_collision_debug.js +++ b/js/render/draw_collision_debug.js @@ -18,7 +18,7 @@ function drawCollisionDebug(painter, source, layer, coords) { if (!bucket.buffers) continue; if (elementGroups[0].vertexLength === 0) continue; - bucket.bindBuffers('collisionBox', gl); + bucket.bindLayoutBuffers('collisionBox', gl); bucket.setAttribPointers('collisionBox', gl, program, elementGroups[0].vertexOffset, layer); painter.setPosMatrix(coord.posMatrix); diff --git a/js/render/draw_fill.js b/js/render/draw_fill.js index b641ea62faf..e61daaace3e 100644 --- a/js/render/draw_fill.js +++ b/js/render/draw_fill.js @@ -115,7 +115,7 @@ function drawFill(painter, source, layer, coord) { var fillProgram = painter.useProgram('fill'); painter.setPosMatrix(translatedPosMatrix); - bucket.bindBuffers('fill', gl); + bucket.bindLayoutBuffers('fill', gl); for (var i = 0; i < elementGroups.length; i++) { var group = elementGroups[i]; @@ -183,7 +183,7 @@ function drawStroke(painter, source, layer, coord) { if (image) { setPattern(image, opacity, tile, coord, painter, program); } // Draw all buffers - bucket.bindBuffers('fill', gl, {secondElement: true}); + bucket.bindLayoutBuffers('fill', gl, {secondElement: true}); painter.enableTileClippingMask(coord); diff --git a/js/render/draw_line.js b/js/render/draw_line.js index 9dc26de15a6..600b845e2c2 100644 --- a/js/render/draw_line.js +++ b/js/render/draw_line.js @@ -163,7 +163,7 @@ module.exports = function drawLine(painter, source, layer, coords) { gl.uniform1f(program.u_ratio, ratio); } - bucket.bindBuffers('line', gl); + bucket.bindLayoutBuffers('line', gl); for (var i = 0; i < elementGroups.length; i++) { var group = elementGroups[i]; diff --git a/js/render/draw_symbol.js b/js/render/draw_symbol.js index 5a928af7aaf..0a01960c488 100644 --- a/js/render/draw_symbol.js +++ b/js/render/draw_symbol.js @@ -145,7 +145,7 @@ function drawSymbol(painter, layer, posMatrix, tile, bucket, elementGroups, pref var group, count; - bucket.bindBuffers(programInterfaceName, gl); + bucket.bindLayoutBuffers(programInterfaceName, gl); if (sdf) { var sdfPx = 8; @@ -179,7 +179,7 @@ function drawSymbol(painter, layer, posMatrix, tile, bucket, elementGroups, pref for (var i = 0; i < elementGroups.length; i++) { group = elementGroups[i]; - bucket.bindBuffers(programInterfaceName, gl); + bucket.bindLayoutBuffers(programInterfaceName, gl); bucket.setAttribPointers(programInterfaceName, gl, program, group.vertexOffset, layer); count = group.elementLength * 3;