diff --git a/src/canvas/canvasRenderer.js b/src/canvas/canvasRenderer.js index 2d0e8090ba..36600d011f 100644 --- a/src/canvas/canvasRenderer.js +++ b/src/canvas/canvasRenderer.js @@ -105,7 +105,9 @@ var canvasRenderer = function (arg) { var features = layer.features(); for (var i = 0; i < features.length; i += 1) { - features[i]._renderOnCanvas(m_this.context2d, map); + if (features[i].visible()) { + features[i]._renderOnCanvas(m_this.context2d, map); + } } }); } diff --git a/src/d3/d3Renderer.js b/src/d3/d3Renderer.js index ad70802fe0..e24b90d8c6 100644 --- a/src/d3/d3Renderer.js +++ b/src/d3/d3Renderer.js @@ -478,6 +478,7 @@ var d3Renderer = function (arg) { data: arg.data, index: arg.dataIndex, style: arg.style, + visible: arg.visible, attributes: arg.attributes, classes: arg.classes, append: arg.append, @@ -538,6 +539,7 @@ var d3Renderer = function (arg) { var data = m_features[id].data, index = m_features[id].index, style = m_features[id].style, + visible = m_features[id].visible, attributes = m_features[id].attributes, classes = m_features[id].classes, append = m_features[id].append, @@ -549,6 +551,9 @@ var d3Renderer = function (arg) { setAttrs(rendersel, attributes); rendersel.attr('class', classes.concat([id]).join(' ')); setStyles(rendersel, style); + if (visible) { + rendersel.style('visibility', visible() ? 'visible' : 'hidden'); + } if (entries.size() && m_features[id].sortByZ) { selection.sort(function (a, b) { return (a.zIndex || 0) - (b.zIndex || 0); diff --git a/src/d3/lineFeature.js b/src/d3/lineFeature.js index 0c101c3670..2e38f88d49 100644 --- a/src/d3/lineFeature.js +++ b/src/d3/lineFeature.js @@ -99,6 +99,7 @@ var d3_lineFeature = function (arg) { }, id: m_this._d3id() + idx, classes: ['d3LineFeature', 'd3SubLine-' + idx], + visible: m_this.visible, style: style }; diff --git a/src/d3/pathFeature.js b/src/d3/pathFeature.js index 26a49e5f0e..6360f5c623 100644 --- a/src/d3/pathFeature.js +++ b/src/d3/pathFeature.js @@ -94,6 +94,7 @@ var d3_pathFeature = function (arg) { 'fill': function () { return false; }, 'fillColor': function () { return { r: 0, g: 0, b: 0 }; } }, s_style); + m_style.visible = m_this.visible; m_this.renderer()._drawFeatures(m_style); diff --git a/src/d3/pointFeature.js b/src/d3/pointFeature.js index eb98fc43f8..9d9fc7253a 100644 --- a/src/d3/pointFeature.js +++ b/src/d3/pointFeature.js @@ -81,6 +81,7 @@ var d3_pointFeature = function (arg) { }; m_style.style = s_style; m_style.classes = ['d3PointFeature']; + m_style.visible = m_this.visible; // pass to renderer to draw m_this.renderer()._drawFeatures(m_style); diff --git a/src/d3/quadFeature.js b/src/d3/quadFeature.js index 2f4c14d9a2..8a5ae7e310 100644 --- a/src/d3/quadFeature.js +++ b/src/d3/quadFeature.js @@ -182,6 +182,7 @@ var d3_quadFeature = function (arg) { }, onlyRenderNew: !this.style('previewColor') && !this.style('previewImage'), sortByZ: true, + visible: m_this.visible, classes: ['d3QuadFeature'] }; renderer._drawFeatures(feature); diff --git a/src/d3/vectorFeature.js b/src/d3/vectorFeature.js index 31e9492313..976748ee3b 100644 --- a/src/d3/vectorFeature.js +++ b/src/d3/vectorFeature.js @@ -242,6 +242,7 @@ var d3_vectorFeature = function (arg) { endStyle: s_style.endStyle }; m_style.classes = ['d3VectorFeature']; + m_style.visible = m_this.visible; // Add markers to the defition list updateMarkers(data, s_style.strokeColor, s_style.strokeOpacity, s_style.originStyle, s_style.endStyle); diff --git a/src/feature.js b/src/feature.js index 6c76fa8de7..d41c94bf23 100644 --- a/src/feature.js +++ b/src/feature.js @@ -41,6 +41,7 @@ var feature = function (arg) { m_dataTime = timestamp(), m_buildTime = timestamp(), m_updateTime = timestamp(), + m_dependentFeatures = [], m_selectedFeatures = []; //////////////////////////////////////////////////////////////////////////// @@ -411,7 +412,8 @@ var feature = function (arg) { this.visible = function (val) { if (val === undefined) { return m_visible; - } else { + } + if (m_visible !== val) { m_visible = val; m_this.modified(); @@ -421,9 +423,25 @@ var feature = function (arg) { } else { m_this._unbindMouseHandlers(); } + for (var i = 0; i < m_dependentFeatures.length; i += 1) { + m_dependentFeatures[i].visible(val); + } + } + return m_this; + }; - return m_this; + //////////////////////////////////////////////////////////////////////////// + /** + * Get/Set a list of dependent features. Dependent features have their + * visibility changed at the same time as the feature. + */ + //////////////////////////////////////////////////////////////////////////// + this.dependentFeatures = function (arg) { + if (arg === undefined) { + return m_dependentFeatures.slice(); } + m_dependentFeatures = arg.slice(); + return m_this; }; //////////////////////////////////////////////////////////////////////////// diff --git a/src/polygonFeature.js b/src/polygonFeature.js index 30675c6aac..f745177bf4 100644 --- a/src/polygonFeature.js +++ b/src/polygonFeature.js @@ -260,6 +260,7 @@ var polygonFeature = function (arg) { if (m_lineFeature && m_this.layer()) { m_this.layer().deleteFeature(m_lineFeature); m_lineFeature = null; + m_this.dependentFeatures([]); } return; } @@ -267,8 +268,12 @@ var polygonFeature = function (arg) { return; } if (!m_lineFeature) { - m_lineFeature = m_this.layer().createFeature( - 'line', {selectionAPI: false, gcs: this.gcs()}); + m_lineFeature = m_this.layer().createFeature('line', { + selectionAPI: false, + gcs: m_this.gcs(), + visible: m_this.visible() + }); + m_this.dependentFeatures([m_lineFeature]); } var polyStyle = m_this.style(); m_lineFeature.style({ @@ -345,6 +350,7 @@ var polygonFeature = function (arg) { if (m_lineFeature && m_this.layer()) { m_this.layer().deleteFeature(m_lineFeature); m_lineFeature = null; + m_this.dependentFeatures([]); } s_exit(); }; diff --git a/tests/cases/feature.js b/tests/cases/feature.js index 36c4a0dfb9..178df220c7 100644 --- a/tests/cases/feature.js +++ b/tests/cases/feature.js @@ -186,6 +186,18 @@ describe('geo.feature', function () { expect(feat.visible(false)).toBe(feat); expect(feat.visible()).toBe(false); expect(feat.getMTime()).toBeGreaterThan(modTime); + + expect(feat.visible(true)).toBe(feat); + var depFeat = geo.feature({layer: layer, renderer: layer.renderer()}); + feat.dependentFeatures([depFeat]); + modTime = depFeat.getMTime(); + expect(feat.visible(false)).toBe(feat); + expect(feat.visible()).toBe(false); + expect(depFeat.visible()).toBe(false); + expect(depFeat.getMTime()).toBeGreaterThan(modTime); + feat.dependentFeatures([]); + expect(feat.visible(true)).toBe(feat); + expect(depFeat.visible()).toBe(false); }); }); describe('Check class accessors', function () { @@ -221,6 +233,12 @@ describe('geo.feature', function () { expect(feat.gcs('EPSG:3857')).toBe(feat); expect(feat.gcs()).toBe('EPSG:3857'); }); + it('dependentFeatures', function () { + expect(feat.dependentFeatures()).toEqual([]); + var depFeat = geo.feature({layer: layer, renderer: layer.renderer()}); + expect(feat.dependentFeatures([depFeat])).toBe(feat); + expect(feat.dependentFeatures()).toEqual([depFeat]); + }); it('bin', function () { expect(feat.bin()).toBe(0); expect(feat.bin(5)).toBe(feat); diff --git a/tests/cases/polygonFeature.js b/tests/cases/polygonFeature.js index 570600aed0..e7eca2662b 100644 --- a/tests/cases/polygonFeature.js +++ b/tests/cases/polygonFeature.js @@ -118,6 +118,25 @@ describe('geo.polygonFeature', function () { polygon._init({style: {data: pos}}); expect(polygon.data()).toEqual(pos); }); + + it('style', function () { + mockVGLRenderer(); + map = create_map(); + // we have to use a valid renderer so that the stroke can be enabled. + layer = map.createLayer('feature', {renderer: 'vgl'}); + polygon = geo.polygonFeature({layer: layer}); + polygon._init(); + expect(polygon.style().stroke).toBe(false); + expect(polygon.dependentFeatures()).toEqual([]); + polygon.style('stroke', true); + expect(polygon.style().stroke).toBe(true); + expect(polygon.dependentFeatures().length).toEqual(1); + polygon.style({stroke: false}); + expect(polygon.style().stroke).toBe(false); + expect(polygon.dependentFeatures()).toEqual([]); + map.deleteLayer(layer); + restoreVGLRenderer(); + }); }); describe('Public utility methods', function () {