diff --git a/src/ol/layer/layer.js b/src/ol/layer/layer.js index 18ec52032a..fae4420727 100644 --- a/src/ol/layer/layer.js +++ b/src/ol/layer/layer.js @@ -68,17 +68,17 @@ ol.layer.Layer.prototype.getSource = function() { }; +/** + * @inheritDoc + */ +ol.layer.Layer.prototype.getSourceState = function() { + return this.getSource().getState(); +}; + + /** * @private */ ol.layer.Layer.prototype.handleSourceChange_ = function() { this.dispatchChangeEvent(); }; - - -/** - * @inheritDoc - */ -ol.layer.Layer.prototype.isReady = function() { - return this.getSource().isReady(); -}; diff --git a/src/ol/layer/layerbase.js b/src/ol/layer/layerbase.js index 3af07a06d1..a6f590de07 100644 --- a/src/ol/layer/layerbase.js +++ b/src/ol/layer/layerbase.js @@ -7,6 +7,7 @@ goog.require('goog.events.EventType'); goog.require('goog.math'); goog.require('goog.object'); goog.require('ol.Object'); +goog.require('ol.source.State'); /** @@ -29,8 +30,8 @@ ol.layer.LayerProperty = { * contrast: number, * hue: number, * opacity: number, - * ready: boolean, * saturation: number, + * sourceState: ol.source.State, * visible: boolean, * maxResolution: number, * minResolution: number}} @@ -142,8 +143,8 @@ ol.layer.Base.prototype.getLayerState = function() { var contrast = this.getContrast(); var hue = this.getHue(); var opacity = this.getOpacity(); - var ready = this.isReady(); var saturation = this.getSaturation(); + var sourceState = this.getSourceState(); var visible = this.getVisible(); var maxResolution = this.getMaxResolution(); var minResolution = this.getMinResolution(); @@ -152,8 +153,8 @@ ol.layer.Base.prototype.getLayerState = function() { 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, + sourceState: sourceState, visible: goog.isDef(visible) ? !!visible : true, maxResolution: goog.isDef(maxResolution) ? maxResolution : Infinity, minResolution: goog.isDef(minResolution) ? Math.max(minResolution, 0) : 0 @@ -232,6 +233,12 @@ goog.exportProperty( ol.layer.Base.prototype.getSaturation); +/** + * @return {ol.source.State} Source state. + */ +ol.layer.Base.prototype.getSourceState = goog.abstractMethod; + + /** * @return {boolean} Visible. */ @@ -248,7 +255,7 @@ goog.exportProperty( * @protected */ ol.layer.Base.prototype.handleLayerChange = function() { - if (this.getVisible() && this.isReady()) { + if (this.getVisible() && this.getSourceState() == ol.source.State.READY) { this.dispatchChangeEvent(); } }; @@ -258,18 +265,12 @@ ol.layer.Base.prototype.handleLayerChange = function() { * @protected */ ol.layer.Base.prototype.handleLayerVisibleChange = function() { - if (this.isReady()) { + if (this.getSourceState() == ol.source.State.READY) { this.dispatchChangeEvent(); } }; -/** - * @return {boolean} Is ready. - */ -ol.layer.Base.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 diff --git a/src/ol/layer/layergroup.js b/src/ol/layer/layergroup.js index 9e37389f54..7336f682a4 100644 --- a/src/ol/layer/layergroup.js +++ b/src/ol/layer/layergroup.js @@ -11,6 +11,7 @@ goog.require('ol.CollectionEvent'); goog.require('ol.CollectionEventType'); goog.require('ol.Object'); goog.require('ol.layer.Base'); +goog.require('ol.source.State'); /** @@ -205,6 +206,7 @@ ol.layer.Group.prototype.getLayerStatesArray = function(opt_obj) { layerState.hue += ownLayerState.hue; layerState.opacity *= ownLayerState.opacity; layerState.saturation *= ownLayerState.saturation; + layerState.sourceState = this.getSourceState(); layerState.visible = layerState.visible && ownLayerState.visible; layerState.maxResolution = Math.min( layerState.maxResolution, ownLayerState.maxResolution); @@ -219,9 +221,31 @@ ol.layer.Group.prototype.getLayerStatesArray = function(opt_obj) { /** * @inheritDoc */ -ol.layer.Group.prototype.isReady = function() { - return null === goog.array.find( - this.getLayers().getArray(), function(elt, index, array) { - return !elt.isReady(); - }); +ol.layer.Group.prototype.getSourceState = function() { + // Return the layer group's source state based on the best source state of its + // children: + // - if any child is READY, return READY + // - otherwise, if any child is LOADING, return LOADING + // - otherwise, all children must be in ERROR, return ERROR + // - otherwise, there are no children, return READY + var layerSourceStates = [0, 0, 0]; + var layers = this.getLayers().getArray(); + var n = layers.length; + var i; + for (i = 0; i < n; ++i) { + var layerSourceState = layers[i].getSourceState(); + goog.asserts.assert(layerSourceState < layerSourceStates.length); + ++layerSourceStates[layerSourceState]; + } + if (layerSourceStates[ol.source.State.READY]) { + return ol.source.State.READY; + } else if (layerSourceStates[ol.source.State.LOADING]) { + return ol.source.State.LOADING; + } else if (layerSourceStates[ol.source.State.ERROR]) { + goog.asserts.assert(layerSourceStates[ol.source.State.ERROR] == n); + return ol.source.State.ERROR; + } else { + goog.asserts.assert(n === 0); + return ol.source.State.READY; + } }; diff --git a/src/ol/renderer/canvas/canvasmaprenderer.js b/src/ol/renderer/canvas/canvasmaprenderer.js index f081990b78..2795c8ab3c 100644 --- a/src/ol/renderer/canvas/canvasmaprenderer.js +++ b/src/ol/renderer/canvas/canvasmaprenderer.js @@ -17,6 +17,7 @@ goog.require('ol.renderer.canvas.ImageLayer'); goog.require('ol.renderer.canvas.TileLayer'); goog.require('ol.renderer.canvas.VectorLayer'); goog.require('ol.size'); +goog.require('ol.source.State'); @@ -121,7 +122,8 @@ ol.renderer.canvas.Map.prototype.renderFrame = function(frameState) { layer = layersArray[i]; layerRenderer = this.getLayerRenderer(layer); layerState = layerStates[goog.getUid(layer)]; - if (!layerState.visible || !layerState.ready || + if (!layerState.visible || + layerState.sourceState != ol.source.State.READY || viewResolution >= layerState.maxResolution || viewResolution < layerState.minResolution) { continue; diff --git a/src/ol/renderer/dom/dommaprenderer.js b/src/ol/renderer/dom/dommaprenderer.js index 1da6b24a6c..3a8dfc49d7 100644 --- a/src/ol/renderer/dom/dommaprenderer.js +++ b/src/ol/renderer/dom/dommaprenderer.js @@ -10,6 +10,7 @@ goog.require('ol.layer.Tile'); goog.require('ol.renderer.Map'); goog.require('ol.renderer.dom.ImageLayer'); goog.require('ol.renderer.dom.TileLayer'); +goog.require('ol.source.State'); @@ -84,7 +85,7 @@ ol.renderer.dom.Map.prototype.renderFrame = function(frameState) { layer = layersArray[i]; layerRenderer = this.getLayerRenderer(layer); layerState = frameState.layerStates[goog.getUid(layer)]; - if (layerState.ready) { + if (layerState.sourceState == ol.source.State.READY) { layerRenderer.renderFrame(frameState, layerState); } } diff --git a/src/ol/renderer/layerrenderer.js b/src/ol/renderer/layerrenderer.js index 65d3d50af6..8dc5cf489a 100644 --- a/src/ol/renderer/layerrenderer.js +++ b/src/ol/renderer/layerrenderer.js @@ -10,6 +10,7 @@ goog.require('ol.TileState'); goog.require('ol.layer.Layer'); goog.require('ol.layer.LayerState'); goog.require('ol.source.Source'); +goog.require('ol.source.State'); goog.require('ol.source.Tile'); @@ -113,7 +114,7 @@ ol.renderer.Layer.prototype.renderFrame = goog.abstractMethod; */ ol.renderer.Layer.prototype.renderIfReadyAndVisible = function() { var layer = this.getLayer(); - if (layer.getVisible() && layer.isReady()) { + if (layer.getVisible() && layer.getSourceState() == ol.source.State.READY) { this.getMap().render(); } }; diff --git a/src/ol/renderer/webgl/webglmaprenderer.js b/src/ol/renderer/webgl/webglmaprenderer.js index f3b1876393..0d0e499cc2 100644 --- a/src/ol/renderer/webgl/webglmaprenderer.js +++ b/src/ol/renderer/webgl/webglmaprenderer.js @@ -27,6 +27,7 @@ goog.require('ol.renderer.webgl.VectorLayer2'); goog.require('ol.renderer.webgl.map.shader.Color'); goog.require('ol.renderer.webgl.map.shader.Default'); goog.require('ol.size'); +goog.require('ol.source.State'); goog.require('ol.structs.Buffer'); goog.require('ol.structs.IntegerSet'); goog.require('ol.structs.LRUCache'); @@ -557,7 +558,8 @@ ol.renderer.webgl.Map.prototype.renderFrame = function(frameState) { layer = layersArray[i]; layerRenderer = this.getLayerRenderer(layer); layerState = frameState.layerStates[goog.getUid(layer)]; - if (layerState.visible && layerState.ready && + if (layerState.visible && + layerState.sourceState == ol.source.State.READY && viewResolution < layerState.maxResolution && viewResolution >= layerState.minResolution) { layerRenderer.renderFrame(frameState, layerState); @@ -585,7 +587,8 @@ ol.renderer.webgl.Map.prototype.renderFrame = function(frameState) { for (i = 0, ii = layersArray.length; i < ii; ++i) { layer = layersArray[i]; layerState = frameState.layerStates[goog.getUid(layer)]; - if (!layerState.visible || !layerState.ready || + if (!layerState.visible || + layerState.sourceState != ol.source.State.READY || viewResolution >= layerState.maxResolution || viewResolution < layerState.minResolution) { continue; diff --git a/src/ol/source/bingmapssource.js b/src/ol/source/bingmapssource.js index 29b3b456ea..54816e51f9 100644 --- a/src/ol/source/bingmapssource.js +++ b/src/ol/source/bingmapssource.js @@ -9,6 +9,7 @@ goog.require('ol.TileRange'); goog.require('ol.TileUrlFunction'); goog.require('ol.extent'); goog.require('ol.proj'); +goog.require('ol.source.State'); goog.require('ol.source.TileImage'); goog.require('ol.tilegrid.XYZ'); @@ -25,6 +26,7 @@ ol.source.BingMaps = function(options) { crossOrigin: 'anonymous', opaque: true, projection: ol.proj.get('EPSG:3857'), + state: ol.source.State.LOADING, tileLoadFunction: options.tileLoadFunction }); @@ -34,12 +36,6 @@ ol.source.BingMaps = function(options) { */ this.culture_ = goog.isDef(options.culture) ? options.culture : 'en-us'; - /** - * @private - * @type {boolean} - */ - this.ready_ = false; - var uri = new goog.Uri( '//dev.virtualearth.net/REST/v1/Imagery/Metadata/' + options.style); var jsonp = new goog.net.Jsonp(uri, 'jsonp'); @@ -139,16 +135,6 @@ ol.source.BingMaps.prototype.handleImageryMetadataResponse = this.setLogo(brandLogoUri); - this.ready_ = true; - - this.dispatchChangeEvent(); + this.setState(ol.source.State.READY); }; - - -/** - * @inheritDoc - */ -ol.source.BingMaps.prototype.isReady = function() { - return this.ready_; -}; diff --git a/src/ol/source/source.js b/src/ol/source/source.js index aa6d20caea..83a4b2ae6d 100644 --- a/src/ol/source/source.js +++ b/src/ol/source/source.js @@ -1,18 +1,29 @@ goog.provide('ol.source.Source'); +goog.provide('ol.source.State'); goog.require('goog.events.EventTarget'); goog.require('goog.events.EventType'); -goog.require('goog.functions'); goog.require('ol.Attribution'); goog.require('ol.Extent'); goog.require('ol.proj'); +/** + * @enum {number} + */ +ol.source.State = { + LOADING: 0, + READY: 1, + ERROR: 2 +}; + + /** * @typedef {{attributions: (Array.|undefined), * extent: (ol.Extent|undefined), * logo: (string|undefined), - * projection: ol.proj.ProjectionLike}} + * projection: ol.proj.ProjectionLike, + * state: (ol.source.State|undefined)}} */ ol.source.SourceOptions; @@ -54,6 +65,13 @@ ol.source.Source = function(options) { */ this.logo_ = options.logo; + /** + * @private + * @type {ol.source.State} + */ + this.state_ = goog.isDef(options.state) ? + options.state : ol.source.State.READY; + /** * @private * @type {number} @@ -120,9 +138,11 @@ ol.source.Source.prototype.getRevision = function() { /** - * @return {boolean} Is ready. + * @return {ol.source.State} State. */ -ol.source.Source.prototype.isReady = goog.functions.TRUE; +ol.source.Source.prototype.getState = function() { + return this.state_; +}; /** @@ -149,6 +169,16 @@ ol.source.Source.prototype.setLogo = function(logo) { }; +/** + * @param {ol.source.State} state State. + * @protected + */ +ol.source.Source.prototype.setState = function(state) { + this.state_ = state; + this.dispatchChangeEvent(); +}; + + /** * @param {ol.proj.Projection} projection Projetion. */ diff --git a/src/ol/source/tilejsonsource.js b/src/ol/source/tilejsonsource.js index 879e68d4a5..e31b9d83e0 100644 --- a/src/ol/source/tilejsonsource.js +++ b/src/ol/source/tilejsonsource.js @@ -15,6 +15,7 @@ goog.require('ol.TileRange'); goog.require('ol.TileUrlFunction'); goog.require('ol.extent'); goog.require('ol.proj'); +goog.require('ol.source.State'); goog.require('ol.source.TileImage'); goog.require('ol.tilegrid.XYZ'); @@ -46,15 +47,10 @@ ol.source.TileJSON = function(options) { goog.base(this, { crossOrigin: options.crossOrigin, projection: ol.proj.get('EPSG:3857'), + state: ol.source.State.LOADING, tileLoadFunction: options.tileLoadFunction }); - /** - * @private - * @type {boolean} - */ - this.ready_ = false; - /** * @private * @type {!goog.async.Deferred} @@ -118,16 +114,6 @@ ol.source.TileJSON.prototype.handleTileJSONResponse = function() { ]); } - this.ready_ = true; - - this.dispatchChangeEvent(); + this.setState(ol.source.State.READY); }; - - -/** - * @inheritDoc - */ -ol.source.TileJSON.prototype.isReady = function() { - return this.ready_; -}; diff --git a/test/spec/ol/layer/layer.test.js b/test/spec/ol/layer/layer.test.js index 473fa96480..bcb52a8be7 100644 --- a/test/spec/ol/layer/layer.test.js +++ b/test/spec/ol/layer/layer.test.js @@ -54,7 +54,7 @@ describe('ol.layer.Layer', function() { opacity: 1, saturation: 1, visible: true, - ready: true, + sourceState: ol.source.State.READY, maxResolution: Infinity, minResolution: 0 }); @@ -96,7 +96,7 @@ describe('ol.layer.Layer', function() { opacity: 0.5, saturation: 5, visible: false, - ready: true, + sourceState: ol.source.State.READY, maxResolution: 500, minResolution: 0.25 }); @@ -138,7 +138,7 @@ describe('ol.layer.Layer', function() { opacity: 0.3, saturation: 0.3, visible: false, - ready: true, + sourceState: ol.source.State.READY, maxResolution: 500, minResolution: 0.25 }); @@ -158,7 +158,7 @@ describe('ol.layer.Layer', function() { opacity: 0, saturation: 0, visible: false, - ready: true, + sourceState: ol.source.State.READY, maxResolution: Infinity, minResolution: 0 }); @@ -176,7 +176,7 @@ describe('ol.layer.Layer', function() { opacity: 1, saturation: 42, visible: true, - ready: true, + sourceState: ol.source.State.READY, maxResolution: Infinity, minResolution: 0 }); @@ -359,3 +359,4 @@ goog.require('goog.dispose'); goog.require('ol.layer.Layer'); goog.require('ol.proj'); goog.require('ol.source.Source'); +goog.require('ol.source.State'); diff --git a/test/spec/ol/layer/layergroup.test.js b/test/spec/ol/layer/layergroup.test.js index 74f9755e46..5946ec1d20 100644 --- a/test/spec/ol/layer/layergroup.test.js +++ b/test/spec/ol/layer/layergroup.test.js @@ -50,7 +50,7 @@ describe('ol.layer.Group', function() { opacity: 1, saturation: 1, visible: true, - ready: true, + sourceState: ol.source.State.READY, maxResolution: Infinity, minResolution: 0 }); @@ -98,7 +98,7 @@ describe('ol.layer.Group', function() { opacity: 0.5, saturation: 5, visible: false, - ready: true, + sourceState: ol.source.State.READY, maxResolution: 500, minResolution: 0.25 }); @@ -140,7 +140,7 @@ describe('ol.layer.Group', function() { opacity: 0.3, saturation: 0.3, visible: false, - ready: true, + sourceState: ol.source.State.READY, maxResolution: 500, minResolution: 0.25 }); @@ -160,7 +160,7 @@ describe('ol.layer.Group', function() { opacity: 0, saturation: 0, visible: false, - ready: true, + sourceState: ol.source.State.READY, maxResolution: Infinity, minResolution: 0 }); @@ -178,7 +178,7 @@ describe('ol.layer.Group', function() { opacity: 1, saturation: 42, visible: true, - ready: true, + sourceState: ol.source.State.READY, maxResolution: Infinity, minResolution: 0 }); @@ -297,7 +297,7 @@ describe('ol.layer.Group', function() { opacity: 0.25, saturation: 25, visible: false, - ready: true, + sourceState: ol.source.State.READY, maxResolution: 150, minResolution: 0.25 }); @@ -316,4 +316,5 @@ goog.require('goog.dispose'); goog.require('ol.layer.Layer'); goog.require('ol.layer.Group'); goog.require('ol.source.Source'); +goog.require('ol.source.State'); goog.require('ol.Collection');