From 2d8201acc095d9668963ee9e18cd7999ed72d5e2 Mon Sep 17 00:00:00 2001 From: John Firebaugh Date: Fri, 28 Oct 2016 14:25:54 -0700 Subject: [PATCH] Get rid of user-specified refs --- bench/benchmarks/buffer.js | 3 +- js/data/bucket.js | 19 ++++- js/data/bucket/symbol_bucket.js | 9 ++- js/data/feature_index.js | 4 +- js/source/tile.js | 4 +- js/style/style.js | 61 ++++------------ js/style/style_layer.js | 53 ++++++-------- js/style/style_layer_index.js | 90 +++++++++-------------- js/ui/map.js | 3 - package.json | 3 +- test/js/source/worker.test.js | 4 +- test/js/style/style.test.js | 58 ++------------- test/js/style/style_layer.test.js | 85 ---------------------- test/js/style/style_layer_index.test.js | 96 +++++++++++++++---------- test/js/ui/map.test.js | 40 ----------- 15 files changed, 159 insertions(+), 373 deletions(-) diff --git a/bench/benchmarks/buffer.js b/bench/benchmarks/buffer.js index ea54c237d2d..c1ed75af8c0 100644 --- a/bench/benchmarks/buffer.js +++ b/bench/benchmarks/buffer.js @@ -14,6 +14,7 @@ const config = require('../../js/util/config'); const coordinates = require('../lib/coordinates'); const formatNumber = require('../lib/format_number'); const accessToken = require('../lib/access_token'); +const deref = require('mapbox-gl-style-spec/lib/deref'); const SAMPLE_COUNT = 10; @@ -125,7 +126,7 @@ function preloadAssets(stylesheet, callback) { } function runSample(stylesheet, getGlyphs, getIcons, getTile, callback) { - const layerIndex = new StyleLayerIndex(stylesheet.layers); + const layerIndex = new StyleLayerIndex(deref(stylesheet.layers)); const timeStart = performance.now(); diff --git a/js/data/bucket.js b/js/data/bucket.js index 30b06d66a96..2f95e41ac8b 100644 --- a/js/data/bucket.js +++ b/js/data/bucket.js @@ -57,8 +57,18 @@ class Bucket { }; } + /** + * Release the WebGL resources associated with the buffers. Note that because + * buckets are shared between layers having the same layout properties, they + * must be destroyed in groups (all buckets for a tile, or all symbol buckets). + * + * @private + */ destroy() { - this.buffers.destroy(); + if (this.buffers) { + this.buffers.destroy(); + this.buffers = null; + } } } @@ -70,6 +80,7 @@ Bucket.deserialize = function(input, style) { if (!style) return; const output = {}; + for (const serialized of input) { const layers = serialized.layerIds .map((id) => style.getLayer(id)) @@ -79,7 +90,11 @@ Bucket.deserialize = function(input, style) { continue; } - output[layers[0].id] = layers[0].createBucket(util.extend({layers}, serialized)); + const bucket = layers[0].createBucket(util.extend({layers}, serialized)); + for (const layer of layers) { + output[layer.id] = bucket; + } } + return output; }; diff --git a/js/data/bucket/symbol_bucket.js b/js/data/bucket/symbol_bucket.js index 918d190e121..c7e0ca41efb 100644 --- a/js/data/bucket/symbol_bucket.js +++ b/js/data/bucket/symbol_bucket.js @@ -213,9 +213,12 @@ class SymbolBucket { } destroy() { - this.buffers.icon.destroy(); - this.buffers.glyph.destroy(); - this.buffers.collisionBox.destroy(); + if (this.buffers) { + this.buffers.icon.destroy(); + this.buffers.glyph.destroy(); + this.buffers.collisionBox.destroy(); + this.buffers = null; + } } createArrays() { diff --git a/js/data/feature_index.js b/js/data/feature_index.js index 01273adffac..3bf2fd9b02d 100644 --- a/js/data/feature_index.js +++ b/js/data/feature_index.js @@ -223,9 +223,7 @@ class FeatureIndex { } const geojsonFeature = new GeoJSONFeature(feature, this.z, this.x, this.y); - geojsonFeature.layer = styleLayer.serialize({ - includeRefProperties: true - }); + geojsonFeature.layer = styleLayer.serialize(); let layerResult = result[layerID]; if (layerResult === undefined) { layerResult = result[layerID] = []; diff --git a/js/source/tile.js b/js/source/tile.js index fee33b42dcc..12da031868a 100644 --- a/js/source/tile.js +++ b/js/source/tile.js @@ -109,13 +109,13 @@ class Tile { for (const id in this.buckets) { this.buckets[id].destroy(); } + this.buckets = {}; this.collisionBoxArray = null; this.symbolQuadsArray = null; this.symbolInstancesArray = null; this.collisionTile = null; this.featureIndex = null; - this.buckets = null; this.state = 'unloaded'; } @@ -152,7 +152,7 @@ class Tile { } getBucket(layer) { - return this.buckets && this.buckets[layer.ref || layer.id]; + return this.buckets[layer.id]; } querySourceFeatures(result, params) { diff --git a/js/style/style.js b/js/style/style.js index 4735a82a7b5..151879e883a 100644 --- a/js/style/style.js +++ b/js/style/style.js @@ -20,6 +20,7 @@ const SourceCache = require('../source/source_cache'); const styleSpec = require('./style_spec'); const MapboxGLFunction = require('mapbox-gl-function'); const getWorkerPool = require('../global_worker_pool'); +const deref = require('mapbox-gl-style-spec/lib/deref'); /** * @private @@ -136,28 +137,15 @@ class Style extends Evented { } _resolve() { - let layer, layerJSON; + const layers = deref(this.stylesheet.layers); - this._layers = {}; - this._order = this.stylesheet.layers.map((layer) => { + this._order = layers.map((layer) => { return layer.id; }); - // resolve all layers WITHOUT a ref - for (let i = 0; i < this.stylesheet.layers.length; i++) { - layerJSON = this.stylesheet.layers[i]; - if (layerJSON.ref) continue; - layer = StyleLayer.create(layerJSON); - this._layers[layer.id] = layer; - layer.setEventedParent(this, {layer: {id: layer.id}}); - } - - // resolve all layers WITH a ref - for (let j = 0; j < this.stylesheet.layers.length; j++) { - layerJSON = this.stylesheet.layers[j]; - if (!layerJSON.ref) continue; - const refLayer = this.getLayer(layerJSON.ref); - layer = StyleLayer.create(layerJSON, refLayer); + this._layers = {}; + for (let layer of layers) { + layer = StyleLayer.create(layer); this._layers[layer.id] = layer; layer.setEventedParent(this, {layer: {id: layer.id}}); } @@ -194,9 +182,8 @@ class Style extends Evented { _serializeLayers(ids) { ids = ids || this._order; const serialized = []; - const options = {includeRefProperties: true}; for (let i = 0; i < ids.length; i++) { - serialized.push(this._layers[ids[i]].serialize(options)); + serialized.push(this._layers[ids[i]].serialize()); } return serialized; } @@ -410,8 +397,7 @@ class Style extends Evented { if (this._validate(validateStyle.layer, `layers.${layer.id}`, layer, {arrayIndex: -1}, options)) return this; - const refLayer = layer.ref && this.getLayer(layer.ref); - layer = StyleLayer.create(layer, refLayer); + layer = StyleLayer.create(layer); } this._validateLayer(layer); @@ -442,11 +428,6 @@ class Style extends Evented { if (layer === undefined) { throw new Error('There is no layer with this ID'); } - for (const i in this._layers) { - if (this._layers[i].ref === id) { - this.removeLayer(i); - } - } layer.setEventedParent(null); @@ -472,26 +453,10 @@ class Style extends Evented { return this._layers[id]; } - /** - * If a layer has a `ref` property that makes it derive some values - * from another layer, return that referent layer. Otherwise, - * returns the layer itself. - * @param {string} id the layer's id - * @returns {Layer} the referent layer or the layer itself - * @private - */ - getReferentLayer(id) { - let layer = this.getLayer(id); - if (layer.ref) { - layer = this.getLayer(layer.ref); - } - return layer; - } - setLayerZoomRange(layerId, minzoom, maxzoom) { this._checkLoaded(); - const layer = this.getReferentLayer(layerId); + const layer = this.getLayer(layerId); if (layer.minzoom === minzoom && layer.maxzoom === maxzoom) return this; @@ -507,7 +472,7 @@ class Style extends Evented { setFilter(layerId, filter) { this._checkLoaded(); - const layer = this.getReferentLayer(layerId); + const layer = this.getLayer(layerId); if (filter !== null && this._validate(validateStyle.filter, `layers.${layer.id}.filter`, filter)) return this; @@ -524,13 +489,13 @@ class Style extends Evented { * @private */ getFilter(layer) { - return util.clone(this.getReferentLayer(layer).filter); + return util.clone(this.getLayer(layer).filter); } setLayoutProperty(layerId, name, value) { this._checkLoaded(); - const layer = this.getReferentLayer(layerId); + const layer = this.getLayer(layerId); if (util.deepEqual(layer.getLayoutProperty(name), value)) return this; @@ -546,7 +511,7 @@ class Style extends Evented { * @private */ getLayoutProperty(layer, name) { - return this.getReferentLayer(layer).getLayoutProperty(name); + return this.getLayer(layer).getLayoutProperty(name); } setPaintProperty(layerId, name, value, klass) { diff --git a/js/style/style_layer.js b/js/style/style_layer.js index bac3925af98..4083b1e5b3f 100644 --- a/js/style/style_layer.js +++ b/js/style/style_layer.js @@ -11,21 +11,20 @@ const Evented = require('../util/evented'); const TRANSITION_SUFFIX = '-transition'; class StyleLayer extends Evented { - constructor(layer, refLayer) { + constructor(layer) { super(); - this.set(layer, refLayer); + this.set(layer); } - set(layer, refLayer) { + set(layer) { 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.type = layer.type; + this.source = layer.source; + this.sourceLayer = layer['source-layer']; + this.minzoom = layer.minzoom; + this.maxzoom = layer.maxzoom; + this.filter = layer.filter; this.paint = {}; this.layout = {}; @@ -54,12 +53,8 @@ class StyleLayer extends Evented { } // Resolve layout declarations - if (this.ref) { - this._layoutDeclarations = refLayer._layoutDeclarations; - } else { - for (layoutName in layer.layout) { - this.setLayoutProperty(layoutName, layer.layout[layoutName], options); - } + for (layoutName in layer.layout) { + this.setLayoutProperty(layoutName, layer.layout[layoutName], options); } // set initial layout/paint values @@ -245,13 +240,17 @@ class StyleLayer extends Evented { } } - serialize(options) { + serialize() { const output = { 'id': this.id, - 'ref': this.ref, + 'type': this.type, + 'source': this.source, + 'source-layer': this.sourceLayer, 'metadata': this.metadata, 'minzoom': this.minzoom, - 'maxzoom': this.maxzoom + 'maxzoom': this.maxzoom, + 'filter': this.filter, + 'layout': util.mapObject(this._layoutDeclarations, getDeclarationValue) }; for (const klass in this._paintDeclarations) { @@ -259,16 +258,6 @@ class StyleLayer extends Evented { output[key] = util.mapObject(this._paintDeclarations[klass], getDeclarationValue); } - if (!this.ref || (options && options.includeRefProperties)) { - util.extend(output, { - 'type': this.type, - 'source': this.source, - 'source-layer': this.sourceLayer, - 'filter': this.filter, - 'layout': util.mapObject(this._layoutDeclarations, getDeclarationValue) - }); - } - return util.filterObject(output, (value, key) => { return value !== undefined && !(key === 'layout' && !Object.keys(value).length); }); @@ -339,9 +328,9 @@ const subclasses = { 'symbol': require('./style_layer/symbol_style_layer') }; -StyleLayer.create = function(layer, refLayer) { - const LayerClass = subclasses[(refLayer || layer).type] || StyleLayer; - return new LayerClass(layer, refLayer); +StyleLayer.create = function(layer) { + const LayerClass = subclasses[layer.type] || StyleLayer; + return new LayerClass(layer); }; function getDeclarationValue(declaration) { diff --git a/js/style/style_layer_index.js b/js/style/style_layer_index.js index d869c485275..1f72499ef8f 100644 --- a/js/style/style_layer_index.js +++ b/js/style/style_layer_index.js @@ -1,83 +1,59 @@ 'use strict'; const StyleLayer = require('./style_layer'); +const util = require('../util/util'); const featureFilter = require('feature-filter'); +const stringify = require('json-stable-stringify'); + +function groupByLayout(layers) { + const groups = {}; + + for (const layer of layers) { + const key = stringify([layer.type, layer.source, layer['source-layer'], layer.minzoom, layer.maxzoom, layer.filter, layer.layout]); + let group = groups[key]; + if (!group) { + group = groups[key] = []; + } + group.push(layer); + } + + return util.values(groups); +} class StyleLayerIndex { constructor(layers) { - this.families = []; if (layers) { this.replace(layers); } } replace(layers) { - this.layers = {}; - this.order = []; + this.order = layers.map((layer) => layer.id); + this._layers = {}; this.update(layers); } - _updateLayer(layer) { - const refLayer = layer.ref && this.layers[layer.ref]; - - let styleLayer = this.layers[layer.id]; - if (styleLayer) { - styleLayer.set(layer, refLayer); - } else { - styleLayer = this.layers[layer.id] = StyleLayer.create(layer, refLayer); - } - - styleLayer.updatePaintTransitions({}, {transition: false}); - styleLayer.filter = featureFilter(styleLayer.filter); - } - update(layers) { for (const layer of layers) { - if (!this.layers[layer.id]) { - this.order.push(layer.id); - } - } - - // Update ref parents - for (const layer of layers) { - if (!layer.ref) this._updateLayer(layer); - } - - // Update ref children - for (const layer of layers) { - if (layer.ref) this._updateLayer(layer); + this._layers[layer.id] = layer; } - this.families = []; - const byParent = {}; - - for (const id of this.order) { - const layer = this.layers[id]; - const parent = layer.ref ? this.layers[layer.ref] : layer; + this.familiesBySource = {}; - if (parent.layout && parent.layout.visibility === 'none') { + const groups = groupByLayout(util.values(this._layers)); + for (let layers of groups) { + layers = layers.map((layer) => { + layer = StyleLayer.create(layer); + layer.updatePaintTransitions({}, {transition: false}); + layer.filter = featureFilter(layer.filter); + return layer; + }); + + const layer = layers[0]; + if (layer.layout && layer.layout.visibility === 'none') { continue; } - let family = byParent[parent.id]; - if (!family) { - family = []; - this.families.push(family); - byParent[parent.id] = family; - } - - if (layer.ref) { - family.push(layer); - } else { - family.unshift(layer); - } - } - - this.familiesBySource = {}; - - for (const family of this.families) { - const layer = family[0]; - const sourceId = layer.source || ''; let sourceGroup = this.familiesBySource[sourceId]; if (!sourceGroup) { @@ -90,7 +66,7 @@ class StyleLayerIndex { sourceLayerFamilies = sourceGroup[sourceLayerId] = []; } - sourceLayerFamilies.push(family); + sourceLayerFamilies.push(layers); } } } diff --git a/js/ui/map.js b/js/ui/map.js index 639cf4a717f..e47048fd010 100755 --- a/js/ui/map.js +++ b/js/ui/map.js @@ -730,9 +730,6 @@ class Map extends Camera { /** * Removes a layer from the map's style. * - * Also removes any layers which refer to the specified layer via a - * [`ref` property](https://www.mapbox.com/mapbox-gl-style-spec/#layer-ref). - * * @param {string} id The ID of the layer to remove. * @throws {Error} if no layer with the specified `id` exists. * @returns {Map} `this` diff --git a/package.json b/package.json index fd701529568..1b782d3a815 100644 --- a/package.json +++ b/package.json @@ -21,9 +21,10 @@ "geojson-vt": "^2.4.0", "gl-matrix": "^2.3.1", "grid-index": "^1.0.0", + "json-stable-stringify": "^1.0.1", "mapbox-gl-function": "mapbox/mapbox-gl-function#41c6724e2bbd7bd1eb5991451bbf118b7d02b525", "mapbox-gl-shaders": "mapbox/mapbox-gl-shaders#abd16b9e8b5da8efbbcad7a83f08491db4777c11", - "mapbox-gl-style-spec": "mapbox/mapbox-gl-style-spec#7fd0b3107e747745f14162a5b07202a8b74b3a43", + "mapbox-gl-style-spec": "mapbox/mapbox-gl-style-spec#15c3c90c5d6cb245734192f0244e002eb6747f7c", "mapbox-gl-supported": "^1.2.0", "package-json-versionify": "^1.0.2", "pbf": "^1.3.2", diff --git a/test/js/source/worker.test.js b/test/js/source/worker.test.js index 24e89acb77d..4591a39e180 100644 --- a/test/js/source/worker.test.js +++ b/test/js/source/worker.test.js @@ -52,9 +52,7 @@ test('isolates different instances\' data', (t) => { { id: 'two', type: 'circle' }, ]); - t.equal(worker.layerIndexes[0].families.length, 1); - t.equal(worker.layerIndexes[1].families.length, 2); - + t.notEqual(worker.layerIndexes[0], worker.layerIndexes[1]); t.end(); }); diff --git a/test/js/style/style.test.js b/test/js/style/style.test.js index 0656325b985..5ae0ca231d5 100644 --- a/test/js/style/style.test.js +++ b/test/js/style/style.test.js @@ -786,7 +786,7 @@ test('Style#removeLayer', (t) => { }); }); - t.test('removes referring layers', (t) => { + t.test('does not remove dereffed layers', (t) => { const style = new Style(createStyleJSON({ layers: [{ id: 'a', @@ -799,8 +799,8 @@ test('Style#removeLayer', (t) => { style.on('style.load', () => { style.removeLayer('a'); - t.deepEqual(style.getLayer('a'), undefined); - t.deepEqual(style.getLayer('b'), undefined); + t.equal(style.getLayer('a'), undefined); + t.notEqual(style.getLayer('b'), undefined); t.end(); }); }); @@ -816,8 +816,7 @@ test('Style#setFilter', (t) => { geojson: createGeoJSONSource() }, layers: [ - { id: 'symbol', type: 'symbol', source: 'geojson', filter: ['==', 'id', 0] }, - { id: 'symbol-child', ref: 'symbol' } + { id: 'symbol', type: 'symbol', source: 'geojson', filter: ['==', 'id', 0] } ] }); } @@ -876,22 +875,6 @@ test('Style#setFilter', (t) => { }); }); - t.test('sets filter on parent', (t) => { - const style = createStyle(); - - style.on('style.load', () => { - style.dispatcher.broadcast = function(key, value) { - t.equal(key, 'updateLayers'); - t.deepEqual(value.map((layer) => { return layer.id; }), ['symbol']); - }; - - style.setFilter('symbol-child', ['==', 'id', 1]); - t.deepEqual(style.getFilter('symbol'), ['==', 'id', 1]); - t.deepEqual(style.getFilter('symbol-child'), ['==', 'id', 1]); - t.end(); - }); - }); - t.test('throws if style is not loaded', (t) => { const style = createStyle(); @@ -927,9 +910,6 @@ test('Style#setLayerZoomRange', (t) => { "id": "symbol", "type": "symbol", "source": "geojson" - }, { - "id": "symbol-child", - "ref": "symbol" }] }); } @@ -950,22 +930,6 @@ test('Style#setLayerZoomRange', (t) => { }); }); - t.test('sets zoom range on parent layer', (t) => { - const style = createStyle(); - - style.on('style.load', () => { - style.dispatcher.broadcast = function(key, value) { - t.equal(key, 'updateLayers'); - t.deepEqual(value.map((layer) => { return layer.id; }), ['symbol']); - }; - - style.setLayerZoomRange('symbol-child', 5, 12); - t.equal(style.getLayer('symbol').minzoom, 5, 'set minzoom'); - t.equal(style.getLayer('symbol').maxzoom, 12, 'set maxzoom'); - t.end(); - }); - }); - t.test('throw before loaded', (t) => { const style = createStyle(); t.throws(() => { @@ -1102,17 +1066,6 @@ test('Style#queryRenderedFeatures', (t) => { t.end(); }); - t.test('ref layer inherits properties', (t) => { - const results = style.queryRenderedFeatures([{column: 1, row: 1, zoom: 1}], {}, 0, 0); - const layer = results[1].layer; - const refLayer = results[0].layer; - t.deepEqual(layer.layout, refLayer.layout); - t.deepEqual(layer.type, refLayer.type); - t.deepEqual(layer.id, refLayer.ref); - t.notEqual(layer.paint, refLayer.paint); - t.end(); - }); - t.test('includes metadata', (t) => { const results = style.queryRenderedFeatures([{column: 1, row: 1, zoom: 1}], {}, 0, 0); @@ -1212,9 +1165,6 @@ test('Style#query*Features', (t) => { "id": "symbol", "type": "symbol", "source": "geojson" - }, { - "id": "symbol-child", - "ref": "symbol" }] }); diff --git a/test/js/style/style_layer.test.js b/test/js/style/style_layer.test.js index 2455e596c9d..5bce3ea710a 100644 --- a/test/js/style/style_layer.test.js +++ b/test/js/style/style_layer.test.js @@ -6,16 +6,6 @@ const FillStyleLayer = require('../../../js/style/style_layer/fill_style_layer') const util = require('../../../js/util/util'); test('StyleLayer', (t) => { - t.test('sets properties from ref', (t) => { - const layer = StyleLayer.create( - {ref: 'ref'}, - StyleLayer.create({type: 'fill'}) - ); - - t.equal(layer.type, 'fill'); - t.end(); - }); - t.test('instantiates the correct subclass', (t) => { const layer = StyleLayer.create({type: 'fill'}); @@ -364,16 +354,6 @@ test('StyleLayer#serialize', (t) => { }, layer); } - function createRefedSymbolLayer(layer) { - return util.extend({ - id: 'symbol', - ref: 'symbol', - paint: { - 'text-color': 'red' - } - }, layer); - } - t.test('serializes layers', (t) => { t.deepEqual( StyleLayer.create(createSymbolLayer()).serialize(), @@ -395,34 +375,6 @@ test('StyleLayer#serialize', (t) => { t.end(); }); - t.test('serializes refed layers', (t) => { - t.deepEqual( - StyleLayer.create( - createRefedSymbolLayer(), - StyleLayer.create(createSymbolLayer()) - ).serialize(), - createRefedSymbolLayer() - ); - t.end(); - }); - - t.test('serializes refed layers with ref properties', (t) => { - t.deepEqual( - StyleLayer.create( - createRefedSymbolLayer(), - StyleLayer.create(createSymbolLayer()) - ).serialize({includeRefProperties: true}), - { - id: "symbol", - type: "symbol", - paint: { "text-color": "red" }, - layout: { "text-transform": "uppercase" }, - ref: "symbol" - } - ); - t.end(); - }); - t.test('serializes functions', (t) => { const layerPaint = { 'text-color': { @@ -476,16 +428,6 @@ test('StyleLayer#serialize', (t) => { }, layer); } - function createRefedSymbolLayer(layer) { - return util.extend({ - id: 'symbol', - ref: 'symbol', - paint: { - 'text-color': 'red' - } - }, layer); - } - t.test('serializes layers', (t) => { t.deepEqual( StyleLayer.create(createSymbolLayer()).serialize(), @@ -494,33 +436,6 @@ test('StyleLayer#serialize', (t) => { t.end(); }); - t.test('serializes refed layers', (t) => { - t.deepEqual( - StyleLayer.create( - createRefedSymbolLayer(), - StyleLayer.create(createSymbolLayer())).serialize(), - createRefedSymbolLayer() - ); - t.end(); - }); - - t.test('serializes refed layers with ref properties', (t) => { - t.deepEqual( - StyleLayer.create( - createRefedSymbolLayer(), - StyleLayer.create(createSymbolLayer()) - ).serialize({includeRefProperties: true}), - { - id: "symbol", - type: "symbol", - paint: { "text-color": "red" }, - layout: { "text-transform": "uppercase" }, - ref: "symbol" - } - ); - t.end(); - }); - t.test('serializes functions', (t) => { const layerPaint = { 'text-color': { diff --git a/test/js/style/style_layer_index.test.js b/test/js/style/style_layer_index.test.js index 363c106a774..40b5b8104db 100644 --- a/test/js/style/style_layer_index.test.js +++ b/test/js/style/style_layer_index.test.js @@ -1,55 +1,52 @@ 'use strict'; const test = require('mapbox-gl-js-test').test; +const util = require('../../../js/util/util'); const StyleLayerIndex = require('../../../js/style/style_layer_index'); -test('StyleLayerIndex', (t) => { - const index = new StyleLayerIndex(); - t.deepEqual(index.families, []); - t.end(); -}); - test('StyleLayerIndex#replace', (t) => { const index = new StyleLayerIndex([ - { 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' } } + { id: '1', type: 'fill', source: 'source', 'source-layer': 'layer', paint: { 'fill-color': 'red' } }, + { id: '2', type: 'circle', source: 'source', 'source-layer': 'layer', paint: { 'circle-color': 'green' } }, + { id: '3', type: 'circle', source: 'source', 'source-layer': 'layer', paint: { 'circle-color': 'blue' } } ]); - t.equal(index.families.length, 2); - t.equal(index.families[0].length, 1); - t.equal(index.families[0][0].id, 'one'); - t.equal(index.families[1].length, 2); - t.equal(index.families[1][0].id, 'two'); - t.equal(index.families[1][1].id, 'three'); + const families = index.familiesBySource['source']['layer']; + t.equal(families.length, 2); + t.equal(families[0].length, 1); + t.equal(families[0][0].id, '1'); + t.equal(families[1].length, 2); + t.equal(families[1][0].id, '2'); + t.equal(families[1][1].id, '3'); index.replace([]); - t.deepEqual(index.families, []); + t.deepEqual(index.familiesBySource, {}); t.end(); }); test('StyleLayerIndex#update', (t) => { const index = new StyleLayerIndex([ - { id: 'one', type: 'circle', paint: { 'circle-color': 'red' }, 'source': 'foo' }, - { id: 'two', type: 'circle', paint: { 'circle-color': 'green' }, 'source': 'foo' }, - { id: 'three', ref: 'two', type: 'circle', paint: { 'circle-color': 'blue' } } + { id: '1', type: 'fill', source: 'foo', 'source-layer': 'layer', paint: { 'fill-color': 'red' } }, + { id: '2', type: 'circle', source: 'foo', 'source-layer': 'layer', paint: { 'circle-color': 'green' } }, + { id: '3', type: 'circle', source: 'foo', 'source-layer': 'layer', paint: { 'circle-color': 'blue' } } ]); index.update([ - { id: 'one', type: 'circle', paint: { 'circle-color': 'cyan' }, 'source': 'bar' }, - { id: 'two', type: 'circle', paint: { 'circle-color': 'magenta' }, 'source': 'bar' }, - { id: 'three', ref: 'two', type: 'circle', paint: { 'circle-color': 'yellow' } } + { id: '1', type: 'fill', source: 'bar', 'source-layer': 'layer', paint: { 'fill-color': 'cyan' } }, + { id: '2', type: 'circle', source: 'bar', 'source-layer': 'layer', paint: { 'circle-color': 'magenta' } }, + { id: '3', type: 'circle', source: 'bar', 'source-layer': 'layer', paint: { 'circle-color': 'yellow' } } ]); - t.equal(index.families.length, 2); - t.equal(index.families[0].length, 1); - t.equal(index.families[0][0].getPaintProperty('circle-color'), 'cyan'); - t.equal(index.families[1].length, 2); - t.equal(index.families[1][0].getPaintProperty('circle-color'), 'magenta'); - t.equal(index.families[1][0].source, 'bar'); - t.equal(index.families[1][1].getPaintProperty('circle-color'), 'yellow'); - t.equal(index.families[1][1].source, 'bar'); + const families = index.familiesBySource['bar']['layer']; + t.equal(families.length, 2); + t.equal(families[0].length, 1); + t.equal(families[0][0].getPaintProperty('fill-color'), 'cyan'); + t.equal(families[1].length, 2); + t.equal(families[1][0].getPaintProperty('circle-color'), 'magenta'); + t.equal(families[1][0].source, 'bar'); + t.equal(families[1][1].getPaintProperty('circle-color'), 'yellow'); + t.equal(families[1][1].source, 'bar'); t.end(); }); @@ -57,30 +54,51 @@ test('StyleLayerIndex#update', (t) => { test('StyleLayerIndex#familiesBySource', (t) => { const index = new StyleLayerIndex([ { id: '0', 'source': 'A', 'source-layer': 'foo' }, - { id: '1', 'ref': '0'}, - { id: '2', 'source': 'A', 'source-layer': 'foo' }, + { id: '1', 'source': 'A', 'source-layer': 'foo' }, + { id: '2', 'source': 'A', 'source-layer': 'foo', 'minzoom': 1 }, { id: '3', 'source': 'A', 'source-layer': 'bar' }, { id: '4', 'source': 'B', 'source-layer': 'foo' }, { id: '5', 'source': 'geojson' }, { id: '6' } ]); - const layers = index.layers; - t.deepEqual(index.familiesBySource, { + const ids = util.mapObject(index.familiesBySource, (bySource) => { + return util.mapObject(bySource, (families) => { + return families.map((family) => { + return family.map((layer) => layer.id); + }); + }); + }); + + t.deepEqual(ids, { 'A': { - 'foo': [[layers['0'], layers['1']], [layers['2']]], - 'bar': [[layers['3']]] + 'foo': [['0', '1'], ['2']], + 'bar': [['3']] }, 'B': { - 'foo': [[layers['4']]] + 'foo': [['4']] }, 'geojson': { - '_geojsonTileLayer': [[layers['5']]] + '_geojsonTileLayer': [['5']] }, '': { - '_geojsonTileLayer': [[layers['6']]] + '_geojsonTileLayer': [['6']] } }); t.end(); }); + +test('StyleLayerIndex groups families even if layout key order differs', (t) => { + const index = new StyleLayerIndex([ + { id: '0', type: 'line', 'source': 'source', 'source-layer': 'layer', + 'layout': {'line-cap': 'butt', 'line-join': 'miter'} }, + { id: '1', type: 'line', 'source': 'source', 'source-layer': 'layer', + 'layout': {'line-join': 'miter', 'line-cap': 'butt'} } + ]); + + const families = index.familiesBySource['source']['layer']; + t.equal(families[0].length, 2); + + t.end(); +}); diff --git a/test/js/ui/map.test.js b/test/js/ui/map.test.js index 38c44c052a1..48abf60f5c5 100755 --- a/test/js/ui/map.test.js +++ b/test/js/ui/map.test.js @@ -684,46 +684,6 @@ test('Map', (t) => { }); }); - t.test('sets property on parent layer', (t) => { - const map = createMap({ - style: { - "version": 8, - "sources": { - "geojson": { - "type": "geojson", - "data": { - "type": "FeatureCollection", - "features": [] - } - } - }, - "layers": [{ - "id": "symbol", - "type": "symbol", - "source": "geojson", - "layout": { - "text-transform": "uppercase" - } - }, { - "id": "symbol-ref", - "ref": "symbol" - }] - } - }); - - map.on('style.load', () => { - map.style.dispatcher.broadcast = function(key, value) { - t.equal(key, 'updateLayers'); - t.deepEqual(value.map((layer) => { return layer.id; }), ['symbol']); - }; - - map.setLayoutProperty('symbol-ref', 'text-transform', 'lowercase'); - map.style.update(); - t.deepEqual(map.getLayoutProperty('symbol', 'text-transform'), 'lowercase'); - t.end(); - }); - }); - t.test('throw before loaded', (t) => { const map = createMap({ style: {