diff --git a/src/objectliterals.jsdoc b/src/objectliterals.jsdoc index 5231e4d45d..d6cf8de273 100644 --- a/src/objectliterals.jsdoc +++ b/src/objectliterals.jsdoc @@ -46,7 +46,7 @@ * @property {ol.Collection|Array.|undefined} controls * Controls initially added to the map. * @property {ol.Collection|undefined} interactions Interactions. - * @property {Array.|ol.Collection|undefined} layers Layers. + * @property {Array.|ol.Collection|undefined} layers Layers. * @property {ol.RendererHint|undefined} renderer Renderer. * @property {Array.|undefined} renderers Renderers. * @property {Element|string|undefined} target The container for the map. @@ -305,6 +305,16 @@ * @property {number|undefined} threshold Minimal angle to start a rotation. */ +/** + * @typedef {Object} ol.layer.LayerBaseOptions + * @property {number|undefined} brightness Brightness. + * @property {number|undefined} contrast Contrast. + * @property {number|undefined} hue Hue. + * @property {number|undefined} opacity Opacity. + * @property {number|undefined} saturation Saturation. + * @property {boolean|undefined} visible Visibility. + */ + /** * @typedef {Object} ol.layer.LayerOptions * @property {number|undefined} brightness Brightness. @@ -316,6 +326,17 @@ * @property {boolean|undefined} visible Visibility. Default is true (visible). */ +/** + * @typedef {Object} ol.layer.LayerGroupOptions + * @property {number|undefined} brightness Brightness. + * @property {number|undefined} contrast Contrast. + * @property {number|undefined} hue Hue. + * @property {number|undefined} opacity Opacity. + * @property {number|undefined} saturation Saturation. + * @property {boolean|undefined} visible Visibility. + * @property {Array.|ol.Collection|undefined} layers Child layers. + */ + /** * @typedef {Object} ol.layer.TileLayerOptions * @property {number|undefined} brightness Brightness. diff --git a/src/ol/layer/layer.js b/src/ol/layer/layer.js index 4f2d9aadf2..c313c3c5c0 100644 --- a/src/ol/layer/layer.js +++ b/src/ol/layer/layer.js @@ -1,49 +1,26 @@ goog.provide('ol.layer.Layer'); -goog.provide('ol.layer.LayerProperty'); -goog.provide('ol.layer.LayerState'); +goog.require('goog.asserts'); goog.require('goog.events'); goog.require('goog.events.EventType'); -goog.require('goog.math'); goog.require('goog.object'); -goog.require('ol.Object'); +goog.require('ol.layer.LayerBase'); goog.require('ol.source.Source'); -/** - * @enum {string} - */ -ol.layer.LayerProperty = { - BRIGHTNESS: 'brightness', - CONTRAST: 'contrast', - HUE: 'hue', - OPACITY: 'opacity', - SATURATION: 'saturation', - VISIBLE: 'visible' -}; - - -/** - * @typedef {{brightness: number, - * contrast: number, - * hue: number, - * opacity: number, - * ready: boolean, - * saturation: number, - * visible: boolean}} - */ -ol.layer.LayerState; - - /** * @constructor - * @extends {ol.Object} + * @extends {ol.layer.LayerBase} * @param {ol.layer.LayerOptions} options Layer options. */ ol.layer.Layer = function(options) { - goog.base(this); + var baseOptions = /** @type {ol.layer.LayerOptions} */ + (goog.object.clone(options)); + delete baseOptions.source; + + goog.base(this, baseOptions); /** * @private @@ -51,31 +28,13 @@ ol.layer.Layer = function(options) { */ this.source_ = options.source; - var values = goog.object.clone(options); - delete values.source; - - /** @type {number} */ - values.brightness = goog.isDef(values.brightness) ? values.brightness : 0; - /** @type {number} */ - values.contrast = goog.isDef(values.contrast) ? values.contrast : 1; - /** @type {number} */ - values.hue = goog.isDef(values.hue) ? values.hue : 0; - /** @type {number} */ - values.opacity = goog.isDef(values.opacity) ? values.opacity : 1; - /** @type {number} */ - values.saturation = goog.isDef(values.saturation) ? values.saturation : 1; - /** @type {boolean} */ - values.visible = goog.isDef(values.visible) ? values.visible : true; - - this.setValues(values); - if (!this.source_.isReady()) { goog.events.listenOnce(this.source_, goog.events.EventType.LOAD, this.handleSourceLoad_, false, this); } }; -goog.inherits(ol.layer.Layer, ol.Object); +goog.inherits(ol.layer.Layer, ol.layer.LayerBase); /** @@ -87,88 +46,20 @@ ol.layer.Layer.prototype.dispatchLoadEvent_ = function() { /** - * @return {number} Brightness. + * @inheritDoc */ -ol.layer.Layer.prototype.getBrightness = function() { - return /** @type {number} */ (this.get(ol.layer.LayerProperty.BRIGHTNESS)); -}; -goog.exportProperty( - ol.layer.Layer.prototype, - 'getBrightness', - ol.layer.Layer.prototype.getBrightness); - - -/** - * @return {number} Contrast. - */ -ol.layer.Layer.prototype.getContrast = function() { - return /** @type {number} */ (this.get(ol.layer.LayerProperty.CONTRAST)); -}; -goog.exportProperty( - ol.layer.Layer.prototype, - 'getContrast', - ol.layer.Layer.prototype.getContrast); - - -/** - * @return {number} Hue. - */ -ol.layer.Layer.prototype.getHue = function() { - return /** @type {number} */ (this.get(ol.layer.LayerProperty.HUE)); -}; -goog.exportProperty( - ol.layer.Layer.prototype, - 'getHue', - ol.layer.Layer.prototype.getHue); - - -/** - * @return {ol.layer.LayerState} Layer state. - */ -ol.layer.Layer.prototype.getLayerState = function() { - var brightness = this.getBrightness(); - var contrast = this.getContrast(); - var hue = this.getHue(); - var opacity = this.getOpacity(); - var ready = this.isReady(); - var saturation = this.getSaturation(); - var visible = this.getVisible(); - return { - brightness: goog.isDef(brightness) ? goog.math.clamp(brightness, -1, 1) : 0, - contrast: goog.isDef(contrast) ? Math.max(contrast, 0) : 1, - hue: goog.isDef(hue) ? hue : 0, - opacity: goog.isDef(opacity) ? goog.math.clamp(opacity, 0, 1) : 1, - ready: ready, - saturation: goog.isDef(saturation) ? Math.max(saturation, 0) : 1, - visible: goog.isDef(visible) ? !!visible : true +ol.layer.Layer.prototype.getLayerStatesArray = function(opt_obj) { + var obj = (goog.isDef(opt_obj)) ? opt_obj : { + layers: [], + layerStates: [] }; + goog.asserts.assert(obj.layers.length === obj.layerStates.length); + obj.layers.push(this); + obj.layerStates.push(this.getLayerState()); + return obj; }; -/** - * @return {number} Opacity. - */ -ol.layer.Layer.prototype.getOpacity = function() { - return /** @type {number} */ (this.get(ol.layer.LayerProperty.OPACITY)); -}; -goog.exportProperty( - ol.layer.Layer.prototype, - 'getOpacity', - ol.layer.Layer.prototype.getOpacity); - - -/** - * @return {number} Saturation. - */ -ol.layer.Layer.prototype.getSaturation = function() { - return /** @type {number} */ (this.get(ol.layer.LayerProperty.SATURATION)); -}; -goog.exportProperty( - ol.layer.Layer.prototype, - 'getSaturation', - ol.layer.Layer.prototype.getSaturation); - - /** * @return {ol.source.Source} Source. */ @@ -177,18 +68,6 @@ ol.layer.Layer.prototype.getSource = function() { }; -/** - * @return {boolean} Visible. - */ -ol.layer.Layer.prototype.getVisible = function() { - return /** @type {boolean} */ (this.get(ol.layer.LayerProperty.VISIBLE)); -}; -goog.exportProperty( - ol.layer.Layer.prototype, - 'getVisible', - ol.layer.Layer.prototype.getVisible); - - /** * @private */ @@ -198,108 +77,8 @@ ol.layer.Layer.prototype.handleSourceLoad_ = function() { /** - * @return {boolean} Is ready. + * @inheritDoc */ ol.layer.Layer.prototype.isReady = function() { return this.getSource().isReady(); }; - - -/** - * Adjust the layer brightness. A value of -1 will render the layer completely - * black. A value of 0 will leave the brightness unchanged. A value of 1 will - * render the layer completely white. Other values are linear multipliers on - * the effect (values are clamped between -1 and 1). - * - * The filter effects draft [1] says the brightness function is supposed to - * render 0 black, 1 unchanged, and all other values as a linear multiplier. - * - * The current WebKit implementation clamps values between -1 (black) and 1 - * (white) [2]. There is a bug open to change the filter effect spec [3]. - * - * TODO: revisit this if the spec is still unmodified before we release - * - * [1] https://dvcs.w3.org/hg/FXTF/raw-file/tip/filters/index.html - * [2] https://github.com/WebKit/webkit/commit/8f4765e569 - * [3] https://www.w3.org/Bugs/Public/show_bug.cgi?id=15647 - * - * @param {number} brightness Brightness. - */ -ol.layer.Layer.prototype.setBrightness = function(brightness) { - this.set(ol.layer.LayerProperty.BRIGHTNESS, brightness); -}; -goog.exportProperty( - ol.layer.Layer.prototype, - 'setBrightness', - ol.layer.Layer.prototype.setBrightness); - - -/** - * Adjust the layer contrast. A value of 0 will render the layer completely - * grey. A value of 1 will leave the contrast unchanged. Other values are - * linear multipliers on the effect (and values over 1 are permitted). - * - * @param {number} contrast Contrast. - */ -ol.layer.Layer.prototype.setContrast = function(contrast) { - this.set(ol.layer.LayerProperty.CONTRAST, contrast); -}; -goog.exportProperty( - ol.layer.Layer.prototype, - 'setContrast', - ol.layer.Layer.prototype.setContrast); - - -/** - * Apply a hue-rotation to the layer. A value of 0 will leave the hue - * unchanged. Other values are radians around the color circle. - * @param {number} hue Hue. - */ -ol.layer.Layer.prototype.setHue = function(hue) { - this.set(ol.layer.LayerProperty.HUE, hue); -}; -goog.exportProperty( - ol.layer.Layer.prototype, - 'setHue', - ol.layer.Layer.prototype.setHue); - - -/** - * @param {number} opacity Opacity. - */ -ol.layer.Layer.prototype.setOpacity = function(opacity) { - this.set(ol.layer.LayerProperty.OPACITY, opacity); -}; -goog.exportProperty( - ol.layer.Layer.prototype, - 'setOpacity', - ol.layer.Layer.prototype.setOpacity); - - -/** - * Adjust layer saturation. A value of 0 will render the layer completely - * unsaturated. A value of 1 will leave the saturation unchanged. Other - * values are linear multipliers of the effect (and values over 1 are - * permitted). - * - * @param {number} saturation Saturation. - */ -ol.layer.Layer.prototype.setSaturation = function(saturation) { - this.set(ol.layer.LayerProperty.SATURATION, saturation); -}; -goog.exportProperty( - ol.layer.Layer.prototype, - 'setSaturation', - ol.layer.Layer.prototype.setSaturation); - - -/** - * @param {boolean} visible Visible. - */ -ol.layer.Layer.prototype.setVisible = function(visible) { - this.set(ol.layer.LayerProperty.VISIBLE, visible); -}; -goog.exportProperty( - ol.layer.Layer.prototype, - 'setVisible', - ol.layer.Layer.prototype.setVisible); diff --git a/src/ol/layer/layerbase.js b/src/ol/layer/layerbase.js new file mode 100644 index 0000000000..30aa53b8f1 --- /dev/null +++ b/src/ol/layer/layerbase.js @@ -0,0 +1,277 @@ +goog.provide('ol.layer.LayerBase'); +goog.provide('ol.layer.LayerProperty'); +goog.provide('ol.layer.LayerState'); + +goog.require('goog.math'); +goog.require('goog.object'); +goog.require('ol.Object'); + + +/** + * @enum {string} + */ +ol.layer.LayerProperty = { + BRIGHTNESS: 'brightness', + CONTRAST: 'contrast', + HUE: 'hue', + OPACITY: 'opacity', + SATURATION: 'saturation', + VISIBLE: 'visible' +}; + + +/** + * @typedef {{brightness: number, + * contrast: number, + * hue: number, + * opacity: number, + * ready: boolean, + * saturation: number, + * visible: boolean}} + */ +ol.layer.LayerState; + + + +/** + * @constructor + * @extends {ol.Object} + * @param {ol.layer.LayerBaseOptions} options Layer options. + */ +ol.layer.LayerBase = function(options) { + + goog.base(this); + + var values = goog.object.clone(options); + + /** @type {number} */ + values.brightness = goog.isDef(values.brightness) ? values.brightness : 0; + /** @type {number} */ + values.contrast = goog.isDef(values.contrast) ? values.contrast : 1; + /** @type {number} */ + values.hue = goog.isDef(values.hue) ? values.hue : 0; + /** @type {number} */ + values.opacity = goog.isDef(values.opacity) ? values.opacity : 1; + /** @type {number} */ + values.saturation = goog.isDef(values.saturation) ? values.saturation : 1; + /** @type {boolean} */ + values.visible = goog.isDef(values.visible) ? values.visible : true; + + this.setValues(values); + +}; +goog.inherits(ol.layer.LayerBase, ol.Object); + + +/** + * @return {number} Brightness. + */ +ol.layer.LayerBase.prototype.getBrightness = function() { + return /** @type {number} */ (this.get(ol.layer.LayerProperty.BRIGHTNESS)); +}; +goog.exportProperty( + ol.layer.LayerBase.prototype, + 'getBrightness', + ol.layer.LayerBase.prototype.getBrightness); + + +/** + * @return {number} Contrast. + */ +ol.layer.LayerBase.prototype.getContrast = function() { + return /** @type {number} */ (this.get(ol.layer.LayerProperty.CONTRAST)); +}; +goog.exportProperty( + ol.layer.LayerBase.prototype, + 'getContrast', + ol.layer.LayerBase.prototype.getContrast); + + +/** + * @return {number} Hue. + */ +ol.layer.LayerBase.prototype.getHue = function() { + return /** @type {number} */ (this.get(ol.layer.LayerProperty.HUE)); +}; +goog.exportProperty( + ol.layer.LayerBase.prototype, + 'getHue', + ol.layer.LayerBase.prototype.getHue); + + +/** + * @return {ol.layer.LayerState} Layer state. + */ +ol.layer.LayerBase.prototype.getLayerState = function() { + var brightness = this.getBrightness(); + var contrast = this.getContrast(); + var hue = this.getHue(); + var opacity = this.getOpacity(); + var ready = this.isReady(); + var saturation = this.getSaturation(); + var visible = this.getVisible(); + return { + brightness: goog.isDef(brightness) ? goog.math.clamp(brightness, -1, 1) : 0, + contrast: goog.isDef(contrast) ? Math.max(contrast, 0) : 1, + hue: goog.isDef(hue) ? hue : 0, + opacity: goog.isDef(opacity) ? goog.math.clamp(opacity, 0, 1) : 1, + ready: ready, + saturation: goog.isDef(saturation) ? Math.max(saturation, 0) : 1, + visible: goog.isDef(visible) ? !!visible : true + }; +}; + + +/** + * @param {{ + * layers: Array., + * layerStates: Array.}=} opt_obj Object that store + * both the layers and the layerStates (to be modified in place). + * @return {{ + * layers: Array., + * layerStates: Array.}} Object that store both the + * layers and the layerStates. + */ +ol.layer.LayerBase.prototype.getLayerStatesArray = goog.abstractMethod; + + +/** + * @return {number} Opacity. + */ +ol.layer.LayerBase.prototype.getOpacity = function() { + return /** @type {number} */ (this.get(ol.layer.LayerProperty.OPACITY)); +}; +goog.exportProperty( + ol.layer.LayerBase.prototype, + 'getOpacity', + ol.layer.LayerBase.prototype.getOpacity); + + +/** + * @return {number} Saturation. + */ +ol.layer.LayerBase.prototype.getSaturation = function() { + return /** @type {number} */ (this.get(ol.layer.LayerProperty.SATURATION)); +}; +goog.exportProperty( + ol.layer.LayerBase.prototype, + 'getSaturation', + ol.layer.LayerBase.prototype.getSaturation); + + +/** + * @return {boolean} Visible. + */ +ol.layer.LayerBase.prototype.getVisible = function() { + return /** @type {boolean} */ (this.get(ol.layer.LayerProperty.VISIBLE)); +}; +goog.exportProperty( + ol.layer.LayerBase.prototype, + 'getVisible', + ol.layer.LayerBase.prototype.getVisible); + + +/** + * @return {boolean} Is ready. + */ +ol.layer.LayerBase.prototype.isReady = goog.abstractMethod; + + +/** + * Adjust the layer brightness. A value of -1 will render the layer completely + * black. A value of 0 will leave the brightness unchanged. A value of 1 will + * render the layer completely white. Other values are linear multipliers on + * the effect (values are clamped between -1 and 1). + * + * The filter effects draft [1] says the brightness function is supposed to + * render 0 black, 1 unchanged, and all other values as a linear multiplier. + * + * The current WebKit implementation clamps values between -1 (black) and 1 + * (white) [2]. There is a bug open to change the filter effect spec [3]. + * + * TODO: revisit this if the spec is still unmodified before we release + * + * [1] https://dvcs.w3.org/hg/FXTF/raw-file/tip/filters/index.html + * [2] https://github.com/WebKit/webkit/commit/8f4765e569 + * [3] https://www.w3.org/Bugs/Public/show_bug.cgi?id=15647 + * + * @param {number} brightness Brightness. + */ +ol.layer.LayerBase.prototype.setBrightness = function(brightness) { + this.set(ol.layer.LayerProperty.BRIGHTNESS, brightness); +}; +goog.exportProperty( + ol.layer.LayerBase.prototype, + 'setBrightness', + ol.layer.LayerBase.prototype.setBrightness); + + +/** + * Adjust the layer contrast. A value of 0 will render the layer completely + * grey. A value of 1 will leave the contrast unchanged. Other values are + * linear multipliers on the effect (and values over 1 are permitted). + * + * @param {number} contrast Contrast. + */ +ol.layer.LayerBase.prototype.setContrast = function(contrast) { + this.set(ol.layer.LayerProperty.CONTRAST, contrast); +}; +goog.exportProperty( + ol.layer.LayerBase.prototype, + 'setContrast', + ol.layer.LayerBase.prototype.setContrast); + + +/** + * Apply a hue-rotation to the layer. A value of 0 will leave the hue + * unchanged. Other values are radians around the color circle. + * @param {number} hue Hue. + */ +ol.layer.LayerBase.prototype.setHue = function(hue) { + this.set(ol.layer.LayerProperty.HUE, hue); +}; +goog.exportProperty( + ol.layer.LayerBase.prototype, + 'setHue', + ol.layer.LayerBase.prototype.setHue); + + +/** + * @param {number} opacity Opacity. + */ +ol.layer.LayerBase.prototype.setOpacity = function(opacity) { + this.set(ol.layer.LayerProperty.OPACITY, opacity); +}; +goog.exportProperty( + ol.layer.LayerBase.prototype, + 'setOpacity', + ol.layer.LayerBase.prototype.setOpacity); + + +/** + * Adjust layer saturation. A value of 0 will render the layer completely + * unsaturated. A value of 1 will leave the saturation unchanged. Other + * values are linear multipliers of the effect (and values over 1 are + * permitted). + * + * @param {number} saturation Saturation. + */ +ol.layer.LayerBase.prototype.setSaturation = function(saturation) { + this.set(ol.layer.LayerProperty.SATURATION, saturation); +}; +goog.exportProperty( + ol.layer.LayerBase.prototype, + 'setSaturation', + ol.layer.LayerBase.prototype.setSaturation); + + +/** + * @param {boolean} visible Visible. + */ +ol.layer.LayerBase.prototype.setVisible = function(visible) { + this.set(ol.layer.LayerProperty.VISIBLE, visible); +}; +goog.exportProperty( + ol.layer.LayerBase.prototype, + 'setVisible', + ol.layer.LayerBase.prototype.setVisible); diff --git a/src/ol/layer/layergroup.js b/src/ol/layer/layergroup.js new file mode 100644 index 0000000000..fb839a4dac --- /dev/null +++ b/src/ol/layer/layergroup.js @@ -0,0 +1,210 @@ +goog.provide('ol.layer.LayerGroup'); + +goog.require('goog.array'); +goog.require('goog.asserts'); +goog.require('goog.events'); +goog.require('goog.events.EventType'); +goog.require('goog.math'); +goog.require('goog.object'); +goog.require('ol.Collection'); +goog.require('ol.CollectionEvent'); +goog.require('ol.CollectionEventType'); +goog.require('ol.Object'); +goog.require('ol.layer.Layer'); +goog.require('ol.layer.LayerBase'); + + +/** + * @enum {string} + */ +ol.layer.LayerGroupProperty = { + LAYERS: 'layers' +}; + + + +/** + * @constructor + * @extends {ol.layer.LayerBase} + * @param {ol.layer.LayerGroupOptions=} opt_options Layer options. + */ +ol.layer.LayerGroup = function(opt_options) { + + var options = goog.isDef(opt_options) ? opt_options : {}; + var baseOptions = /** @type {ol.layer.LayerGroupOptions} */ + (goog.object.clone(options)); + delete baseOptions.layers; + + var layers = options.layers; + + goog.base(this, baseOptions); + + /** + * @private + * @type {Object.} + */ + this.listenerKeys_ = null; + + goog.events.listen(this, + ol.Object.getChangeEventType(ol.layer.LayerGroupProperty.LAYERS), + this.handleLayersChanged_, false, this); + + if (goog.isDef(layers)) { + if (goog.isArray(layers)) { + layers = new ol.Collection(goog.array.clone(layers)); + } else { + goog.asserts.assertInstanceof(layers, ol.Collection); + layers = layers; + } + } else { + layers = new ol.Collection(); + } + + this.setLayers(layers); + +}; +goog.inherits(ol.layer.LayerGroup, ol.layer.LayerBase); + + +/** + * @param {goog.events.Event} event Event. + * @private + */ +ol.layer.LayerGroup.prototype.handleLayersChanged_ = function(event) { + if (!goog.isNull(this.listenerKeys_)) { + goog.array.forEach( + goog.object.getValues(this.listenerKeys_), goog.events.unlistenByKey); + this.listenerKeys_ = null; + } + + var layers = this.getLayers(); + if (goog.isDefAndNotNull(layers)) { + this.listenerKeys_ = { + 'add': goog.events.listen(layers, ol.CollectionEventType.ADD, + this.handleLayersAdd_, false, this), + 'remove': goog.events.listen(layers, ol.CollectionEventType.REMOVE, + this.handleLayersRemove_, false, this) + }; + + var layersArray = layers.getArray(); + var i, ii, layer; + for (i = 0, ii = layersArray.length; i < ii; i++) { + layer = layersArray[i]; + this.listenerKeys_[goog.getUid(layer).toString()] = + goog.events.listen(layer, goog.events.EventType.CHANGE, + this.handleLayerChange_, false, this); + } + } + + this.dispatchChangeEvent_(); +}; + + +/** + * @param {ol.CollectionEvent} collectionEvent Collection event. + * @private + */ +ol.layer.LayerGroup.prototype.handleLayersAdd_ = function(collectionEvent) { + var layer = /** @type {ol.layer.LayerBase} */ (collectionEvent.elem); + this.listenerKeys_[goog.getUid(layer).toString()] = goog.events.listen( + layer, goog.events.EventType.CHANGE, this.handleLayerChange_, false, + this); + this.dispatchChangeEvent_(); +}; + + +/** + * @param {ol.CollectionEvent} collectionEvent Collection event. + * @private + */ +ol.layer.LayerGroup.prototype.handleLayersRemove_ = function(collectionEvent) { + var layer = /** @type {ol.layer.LayerBase} */ (collectionEvent.elem); + var key = goog.getUid(layer).toString(); + goog.events.unlistenByKey(this.listenerKeys_[key]); + delete this.listenerKeys_[key]; + this.dispatchChangeEvent_(); +}; + + +/** + * @private + */ +ol.layer.LayerGroup.prototype.handleLayerChange_ = function() { + this.dispatchChangeEvent_(); +}; + + +/** + * @private + */ +ol.layer.LayerGroup.prototype.dispatchChangeEvent_ = function() { + this.dispatchEvent(goog.events.EventType.CHANGE); +}; + + +/** + * @return {ol.Collection} Collection of layers. + */ +ol.layer.LayerGroup.prototype.getLayers = function() { + return /** @type {ol.Collection} */ (this.get( + ol.layer.LayerGroupProperty.LAYERS)); +}; +goog.exportProperty( + ol.layer.LayerGroup.prototype, + 'getLayers', + ol.layer.LayerGroup.prototype.getLayers); + + +/** + * @param {ol.Collection} layers Collection of layers. + */ +ol.layer.LayerGroup.prototype.setLayers = function(layers) { + this.set(ol.layer.LayerGroupProperty.LAYERS, layers); +}; +goog.exportProperty( + ol.layer.LayerGroup.prototype, + 'setLayers', + ol.layer.LayerGroup.prototype.setLayers); + + +/** + * @inheritDoc + */ +ol.layer.LayerGroup.prototype.getLayerStatesArray = function(opt_obj) { + var obj = (goog.isDef(opt_obj)) ? opt_obj : { + layers: [], + layerStates: [] + }; + goog.asserts.assert(obj.layers.length === obj.layerStates.length); + var pos = obj.layers.length; + + this.getLayers().forEach(function(layer) { + layer.getLayerStatesArray(obj); + }); + + var ownLayerState = this.getLayerState(); + var i, ii, layerState; + for (i = pos, ii = obj.layerStates.length; i < ii; i++) { + layerState = obj.layerStates[i]; + layerState.brightness = goog.math.clamp( + layerState.brightness + ownLayerState.brightness, -1, 1); + layerState.contrast *= ownLayerState.contrast; + layerState.hue += ownLayerState.hue; + layerState.opacity *= ownLayerState.opacity; + layerState.saturation *= ownLayerState.saturation; + layerState.visible = layerState.visible && ownLayerState.visible; + } + + return obj; +}; + + +/** + * @inheritDoc + */ +ol.layer.LayerGroup.prototype.isReady = function() { + return null === goog.array.find( + this.getLayers().getArray(), function(elt, index, array) { + return !elt.isReady(); + }); +}; diff --git a/src/ol/map.js b/src/ol/map.js index 3dded4b5d5..563b958b42 100644 --- a/src/ol/map.js +++ b/src/ol/map.js @@ -32,8 +32,6 @@ goog.require('goog.style'); goog.require('goog.vec.Mat4'); goog.require('ol.BrowserFeature'); goog.require('ol.Collection'); -goog.require('ol.CollectionEvent'); -goog.require('ol.CollectionEventType'); goog.require('ol.FrameState'); goog.require('ol.IView'); goog.require('ol.MapBrowserEvent'); @@ -56,6 +54,8 @@ goog.require('ol.control.defaults'); goog.require('ol.extent'); goog.require('ol.interaction.defaults'); goog.require('ol.layer.Layer'); +goog.require('ol.layer.LayerBase'); +goog.require('ol.layer.LayerGroup'); goog.require('ol.proj'); goog.require('ol.proj.addCommonProjections'); goog.require('ol.renderer.Map'); @@ -309,9 +309,12 @@ ol.Map = function(options) { /** * @private - * @type {Array.} + * @type {ol.layer.LayerGroup} */ - this.layersListenerKeys_ = null; + this.layerGroup_ = new ol.layer.LayerGroup(); + + goog.events.listen(this.layerGroup_, ol.ObjectEventType.CHANGE, + this.handleLayerGroupChanged_, false, this); goog.events.listen(this, ol.Object.getChangeEventType(ol.MapProperty.LAYERS), this.handleLayersChanged_, false, this); @@ -352,7 +355,7 @@ ol.Map.prototype.addControl = function(control) { /** * Adds the given layer to the top of this map. - * @param {ol.layer.Layer} layer Layer. + * @param {ol.layer.LayerBase} layer Layer. */ ol.Map.prototype.addLayer = function(layer) { var layers = this.getLayers(); @@ -615,46 +618,6 @@ ol.Map.prototype.handleBrowserEvent = function(browserEvent, opt_type) { }; -/** - * @param {ol.CollectionEvent} collectionEvent Collection event. - * @private - */ -ol.Map.prototype.handleLayersAdd_ = function(collectionEvent) { - this.render(); -}; - - -/** - * @param {goog.events.Event} event Event. - * @private - */ -ol.Map.prototype.handleLayersChanged_ = function(event) { - if (!goog.isNull(this.layersListenerKeys_)) { - goog.array.forEach(this.layersListenerKeys_, goog.events.unlistenByKey); - this.layersListenerKeys_ = null; - } - var layers = this.getLayers(); - if (goog.isDefAndNotNull(layers)) { - this.layersListenerKeys_ = [ - goog.events.listen(layers, ol.CollectionEventType.ADD, - this.handleLayersAdd_, false, this), - goog.events.listen(layers, ol.CollectionEventType.REMOVE, - this.handleLayersRemove_, false, this) - ]; - } - this.render(); -}; - - -/** - * @param {ol.CollectionEvent} collectionEvent Collection event. - * @private - */ -ol.Map.prototype.handleLayersRemove_ = function(collectionEvent) { - this.render(); -}; - - /** * @param {ol.MapBrowserEvent} mapBrowserEvent The event to handle. */ @@ -788,6 +751,23 @@ ol.Map.prototype.handleViewChanged_ = function() { }; +/** + * @param {goog.events.Event} event Event. + * @private + */ +ol.Map.prototype.handleLayersChanged_ = function(event) { + this.layerGroup_.setLayers(this.getLayers()); +}; + + +/** + * @private + */ +ol.Map.prototype.handleLayerGroupChanged_ = function() { + this.render(); +}; + + /** * @return {boolean} Is defined. */ @@ -845,14 +825,14 @@ ol.Map.prototype.removeControl = function(control) { /** * Removes the given layer from the map. - * @param {ol.layer.Layer} layer Layer. - * @return {ol.layer.Layer|undefined} The removed layer or undefined if the + * @param {ol.layer.LayerBase} layer Layer. + * @return {ol.layer.LayerBase|undefined} The removed layer or undefined if the * layer was not found. */ ol.Map.prototype.removeLayer = function(layer) { var layers = this.getLayers(); goog.asserts.assert(goog.isDef(layers)); - return /** @type {ol.layer.Layer|undefined} */ (layers.remove(layer)); + return /** @type {ol.layer.LayerBase|undefined} */ (layers.remove(layer)); }; @@ -869,21 +849,20 @@ ol.Map.prototype.renderFrame_ = function(time) { } var size = this.getSize(); - var layers = this.getLayers(); - var layersArray = goog.isDef(layers) ? - /** @type {Array.} */ (layers.getArray()) : undefined; var view = this.getView(); var view2D = goog.isDef(view) ? this.getView().getView2D() : undefined; /** @type {?ol.FrameState} */ var frameState = null; - if (goog.isDef(layersArray) && goog.isDef(size) && goog.isDef(view2D) && - view2D.isDef()) { + if (goog.isDef(size) && goog.isDef(view2D) && view2D.isDef()) { var viewHints = view.getHints(); + var obj = this.layerGroup_.getLayerStatesArray(); + var layersArray = obj.layers; + var layerStatesArray = obj.layerStates; var layerStates = {}; var layer; for (i = 0, ii = layersArray.length; i < ii; ++i) { layer = layersArray[i]; - layerStates[goog.getUid(layer)] = layer.getLayerState(); + layerStates[goog.getUid(layer)] = layerStatesArray[i]; } view2DState = view2D.getView2DState(); frameState = {