From e52cbf7a599fc20fbbb6361aee64172ce4be80bd Mon Sep 17 00:00:00 2001 From: David Manthey Date: Tue, 28 Mar 2017 13:12:22 -0400 Subject: [PATCH] Add a visible() function to layers. Note that, much like feature level visibility, you might need to call layer.draw() after making the layer visible to ensure an update. For feature layers, visibility cascades down to individual features (which disables their interaction). --- src/feature.js | 23 ++++++++++++++----- src/featureLayer.js | 45 ++++++++++++++++++++++++++++++++----- src/gl/quadFeature.js | 4 ++-- src/layer.js | 23 +++++++++++++++++++ src/pixelmapFeature.js | 11 ++++----- src/pointFeature.js | 4 ---- src/polygonFeature.js | 2 +- src/tileLayer.js | 38 ++++++++++++++++++++++++++----- tests/cases/feature.js | 14 ++++++++++++ tests/cases/featureLayer.js | 13 +++++++++++ tests/cases/tileLayer.js | 25 +++++++++++++++++++++ 11 files changed, 174 insertions(+), 28 deletions(-) diff --git a/src/feature.js b/src/feature.js index a0f48ef41a..63ead89bc4 100644 --- a/src/feature.js +++ b/src/feature.js @@ -413,24 +413,37 @@ var feature = function (arg) { //////////////////////////////////////////////////////////////////////////// /** * Get/Set visibility of the feature + * + * @param {boolean|undefined} val: undefined to return the visibility, a + * boolean to change the visibility. + * @param {boolean} direct: if true, when getting the visibility, disregard + * the visibility of the parent layer, and when setting, refresh the state + * regardless of whether it has changed or not. + * @return {boolean|object} either the visibility (if getting) or the feature + * (if setting). */ //////////////////////////////////////////////////////////////////////////// - this.visible = function (val) { + this.visible = function (val, direct) { if (val === undefined) { + if (!direct && m_layer && m_layer.visible && !m_layer.visible()) { + return false; + } return m_visible; } - if (m_visible !== val) { + if (m_visible !== val || direct) { m_visible = val; m_this.modified(); - + if (m_layer && m_layer.visible && !m_layer.visible()) { + val = false; + } // bind or unbind mouse handlers on visibility change - if (m_visible) { + if (val) { m_this._bindMouseHandlers(); } else { m_this._unbindMouseHandlers(); } for (var i = 0; i < m_dependentFeatures.length; i += 1) { - m_dependentFeatures[i].visible(val); + m_dependentFeatures[i].visible(m_visible, direct); } } return m_this; diff --git a/src/featureLayer.js b/src/featureLayer.js index 1f2fa62dc9..84b6d9f899 100644 --- a/src/featureLayer.js +++ b/src/featureLayer.js @@ -30,6 +30,7 @@ var featureLayer = function (arg) { s_init = this._init, s_exit = this._exit, s_update = this._update, + s_visible = this.visible, s_draw = this.draw; //////////////////////////////////////////////////////////////////////////// @@ -231,13 +232,45 @@ var featureLayer = function (arg) { */ //////////////////////////////////////////////////////////////////////////// this.draw = function () { - // Call sceneObject.draw, which calls draw on all child objects. - s_draw(); + if (m_this.visible()) { + // Call sceneObject.draw, which calls draw on all child objects. + s_draw(); - // Now call render on the renderer. In certain cases it may not do - // anything if the if the child objects are drawn on the screen already. - if (m_this.renderer()) { - m_this.renderer()._render(); + // Now call render on the renderer. In certain cases it may not do + // anything if the child objects are drawn on the screen already. + if (m_this.renderer()) { + m_this.renderer()._render(); + } + } + return m_this; + }; + + //////////////////////////////////////////////////////////////////////////// + /** + * Get/Set visibility of the layer + * + * @param {boolean|undefined} val: undefined to return the visibility, a + * boolean to change the visibility. + * @return {boolean|object} either the visibility (if getting) or the layer + * (if setting). + */ + //////////////////////////////////////////////////////////////////////////// + this.visible = function (val) { + if (val === undefined) { + return s_visible(); + } + if (m_this.visible() !== val) { + s_visible(val); + + // take a copy of the features; changing visible could mutate them. + var features = m_features.slice(), i; + + for (i = 0; i < features.length; i += 1) { + features[i].visible(features[i].visible(undefined, true), true); + } + if (val) { + m_this.draw(); + } } return m_this; }; diff --git a/src/gl/quadFeature.js b/src/gl/quadFeature.js index 6be4e59c40..55fe0a8f52 100644 --- a/src/gl/quadFeature.js +++ b/src/gl/quadFeature.js @@ -373,11 +373,11 @@ var gl_quadFeature = function (arg) { m_this._build(); } if (m_actor_color) { - m_actor_color.setVisible(m_this.visible()); + m_actor_color.setVisible(m_this.visible(undefined, true)); m_actor_color.material().setBinNumber(m_this.bin()); } if (m_actor_image) { - m_actor_image.setVisible(m_this.visible()); + m_actor_image.setVisible(m_this.visible(undefined, true)); m_actor_image.material().setBinNumber(m_this.bin()); } m_this.updateTime().modified(); diff --git a/src/layer.js b/src/layer.js index 74cf53a6ae..46f55c4a56 100644 --- a/src/layer.js +++ b/src/layer.js @@ -55,6 +55,7 @@ var layer = function (arg) { m_active = arg.active === undefined ? true : arg.active, m_opacity = arg.opacity === undefined ? 1 : arg.opacity, m_attribution = arg.attribution || null, + m_visible = arg.visible === undefined ? true : arg.visible, m_zIndex; m_rendererName = checkRenderer(m_rendererName); @@ -352,6 +353,28 @@ var layer = function (arg) { return m_attribution; }; + //////////////////////////////////////////////////////////////////////////// + /** + * Get/Set visibility of the layer + * + * @param {boolean|undefined} val: undefined to return the visibility, a + * boolean to change the visibility. + * @return {boolean|object} either the visibility (if getting) or the layer + * (if setting). + */ + //////////////////////////////////////////////////////////////////////////// + this.visible = function (val) { + if (val === undefined) { + return m_visible; + } + if (m_visible !== val) { + m_visible = val; + m_node.css('display', m_visible ? '' : 'none'); + m_this.modified(); + } + return m_this; + }; + //////////////////////////////////////////////////////////////////////////// /** * Init layer diff --git a/src/pixelmapFeature.js b/src/pixelmapFeature.js index 43c5edb237..facb46bc5d 100644 --- a/src/pixelmapFeature.js +++ b/src/pixelmapFeature.js @@ -339,13 +339,14 @@ var pixelmapFeature = function (arg) { m_quadFeature = m_this.layer().createFeature('quad', { selectionAPI: false, gcs: m_this.gcs(), - visible: m_this.visible() + visible: m_this.visible(undefined, true) }); m_this.dependentFeatures([m_quadFeature]); - m_quadFeature.style({image: m_info.canvas, - position: m_this.style.get('position')}) - .data([{}]) - .draw(); + m_quadFeature.style({ + image: m_info.canvas, + position: m_this.style.get('position')}) + .data([{}]) + .draw(); } /* If we prepared the pixelmap and rendered it, send a prepared event */ if (prepared) { diff --git a/src/pointFeature.js b/src/pointFeature.js index ca557802d0..7d41f6c65e 100644 --- a/src/pointFeature.js +++ b/src/pointFeature.js @@ -224,10 +224,6 @@ var pointFeature = function (arg) { strokeWidth = m_this.style.get('strokeWidth'), radius = m_this.style.get('radius'); - if (!m_this.selectionAPI()) { - return []; - } - data = m_this.data(); if (!data || !data.length) { return { diff --git a/src/polygonFeature.js b/src/polygonFeature.js index f745177bf4..3b5bc753af 100644 --- a/src/polygonFeature.js +++ b/src/polygonFeature.js @@ -271,7 +271,7 @@ var polygonFeature = function (arg) { m_lineFeature = m_this.layer().createFeature('line', { selectionAPI: false, gcs: m_this.gcs(), - visible: m_this.visible() + visible: m_this.visible(undefined, true) }); m_this.dependentFeatures([m_lineFeature]); } diff --git a/src/tileLayer.js b/src/tileLayer.js index ac39d2a963..efab5ab1c8 100644 --- a/src/tileLayer.js +++ b/src/tileLayer.js @@ -138,6 +138,11 @@ module.exports = (function () { */ ////////////////////////////////////////////////////////////////////////////// var tileLayer = function (options) { + 'use strict'; + if (!(this instanceof tileLayer)) { + return new tileLayer(options); + } + featureLayer.call(this, options); var $ = require('jquery'); var geo_event = require('./event'); @@ -147,11 +152,6 @@ module.exports = (function () { var adjustLayerForRenderer = require('./registry').adjustLayerForRenderer; var Tile = require('./tile'); - if (!(this instanceof tileLayer)) { - return new tileLayer(options); - } - featureLayer.call(this, options); - options = $.extend(true, {}, this.constructor.defaults, options || {}); if (!options.cacheSize) { // this size should be sufficient for a 4k display @@ -177,6 +177,7 @@ module.exports = (function () { var s_init = this._init, s_exit = this._exit, + s_visible = this.visible, m_lastTileSet = [], m_maxBounds = [], m_exited; @@ -1063,6 +1064,9 @@ module.exports = (function () { evt.event.event === geo_event.rotate)) { return; } + if (!this.visible()) { + return; + } var map = this.map(), bounds = map.bounds(undefined, null), mapZoom = map.zoom(), @@ -1430,6 +1434,30 @@ module.exports = (function () { return m_tileOffsetValues[level]; }; + //////////////////////////////////////////////////////////////////////////// + /** + * Get/Set visibility of the layer + * + * @param {boolean|undefined} val: undefined to return the visibility, a + * boolean to change the visibility. + * @return {boolean|object} either the visibility (if getting) or the layer + * (if setting). + */ + //////////////////////////////////////////////////////////////////////////// + this.visible = function (val) { + if (val === undefined) { + return s_visible(); + } + if (this.visible() !== val) { + s_visible(val); + + if (val) { + this._update(); + } + } + return this; + }; + /** * Initialize after the layer is added to the map. */ diff --git a/tests/cases/feature.js b/tests/cases/feature.js index 1360d89205..2a8ed5c5a6 100644 --- a/tests/cases/feature.js +++ b/tests/cases/feature.js @@ -198,6 +198,20 @@ describe('geo.feature', function () { feat.dependentFeatures([]); expect(feat.visible(true)).toBe(feat); expect(depFeat.visible()).toBe(false); + + // the layer can control the visibility + expect(feat.visible()).toBe(true); + expect(feat.visible(undefined, true)).toBe(true); + layer.visible(false); + expect(feat.visible()).toBe(false); + expect(feat.visible(undefined, true)).toBe(true); + expect(feat.visible(false, true)).toBe(feat); + expect(feat.visible()).toBe(false); + expect(feat.visible(undefined, true)).toBe(false); + layer.visible(true); + expect(feat.visible()).toBe(false); + expect(feat.visible(true, true)).toBe(feat); + expect(feat.visible()).toBe(true); }); }); describe('Check class accessors', function () { diff --git a/tests/cases/featureLayer.js b/tests/cases/featureLayer.js index ec4ba6648c..0e8ab96608 100644 --- a/tests/cases/featureLayer.js +++ b/tests/cases/featureLayer.js @@ -127,6 +127,19 @@ describe('geo.featureLayer', function () { expect(layer.features().length).toBe(2); expect(layer.features()).toEqual([feat2, feat1]); }); + it('visible', function () { + expect(layer.visible()).toBe(true); + expect(feat1.visible()).toBe(true); + expect(feat1.visible(undefined, true)).toBe(true); + expect(layer.visible(false)).toBe(layer); + expect(layer.visible()).toBe(false); + expect(feat1.visible()).toBe(false); + expect(feat1.visible(undefined, true)).toBe(true); + expect(layer.visible(true)).toBe(layer); + expect(layer.visible()).toBe(true); + expect(feat1.visible()).toBe(true); + expect(feat1.visible(undefined, true)).toBe(true); + }); it('draw', function () { sinon.stub(feat1, 'draw', function () {}); expect(layer.draw()).toBe(layer); diff --git a/tests/cases/tileLayer.js b/tests/cases/tileLayer.js index 6a81bbe8e2..428374b290 100644 --- a/tests/cases/tileLayer.js +++ b/tests/cases/tileLayer.js @@ -96,6 +96,8 @@ describe('geo.tileLayer', function () { }, updateAttribution: function () { }, + bounds: function () { + }, node: get_set('node'), children: function () { return []; @@ -415,6 +417,29 @@ describe('geo.tileLayer', function () { expect(l.tilesAtZoom(2)).toEqual({x: 4, y: 3}); expect(l.tilesAtZoom(3)).toEqual({x: 8, y: 6}); }); + it('visible', function () { + var m = map(), layer, count = 0; + opts.map = m; + layer = geo.tileLayer(opts); + // check if we are updating by doing the least possible and tracking it + layer._getTiles = function () { + count += 1; + }; + layer._updateSubLayers = undefined; + + expect(layer.visible()).toBe(true); + layer._update(); + expect(count).toBe(1); + expect(layer.visible(false)).toBe(layer); + expect(layer.visible()).toBe(false); + layer._update(); + expect(count).toBe(1); + expect(layer.visible(true)).toBe(layer); + expect(layer.visible()).toBe(true); + expect(count).toBe(2); + layer._update(); + expect(count).toBe(3); + }); }); describe('Public utility methods', function () { describe('isValid', function () {