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).
MODIFY
src/feature.js
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;

MODIFY
src/featureLayer.js
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,14 +232,46 @@ var featureLayer = function (arg) {
*/
////////////////////////////////////////////////////////////////////////////
this.draw = function () {
+ 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.
+ // 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;
};

MODIFY
src/gl/quadFeature.js
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();

MODIFY
src/layer.js
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

MODIFY
src/pixelmapFeature.js
src/pixelmapFeature.js
@@ -339,10 +339,11 @@ 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,
+ m_quadFeature.style({
+ image: m_info.canvas,
position: m_this.style.get('position')})
.data([{}])
.draw();

MODIFY
src/pointFeature.js
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 {

MODIFY
src/polygonFeature.js
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]);
}

MODIFY
src/tileLayer.js
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.
*/

MODIFY
tests/cases/feature.js
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 () {

MODIFY
tests/cases/featureLayer.js
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);

MODIFY
tests/cases/tileLayer.js
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 () {

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).