diff --git a/examples/lazy-source.html b/examples/lazy-source.html new file mode 100644 index 0000000000..ce57a051d5 --- /dev/null +++ b/examples/lazy-source.html @@ -0,0 +1,73 @@ + + + + + + + + + + + Lazy Source + + + + + + +
+ +
+
+
+
+
+ +
+ +
+

Lazy source example

+

Example of setting a layer source after construction.

+
+

+ Typically, the source for a layer is provided to the layer constructor. + If you need to set a layer source after construction, this can be + done with the layer.setSource() method. +

+

+ The layer in the map above is constructed with no source. Use the + links below to set/unset the layer source. A layer is not rendered + until its source is set. +

+

+ + +

+

See the lazy-source.js source for details on how this is done.

+
+
source
+
+ +
+ +
+ + + + + + + diff --git a/examples/lazy-source.js b/examples/lazy-source.js new file mode 100644 index 0000000000..b53347bb80 --- /dev/null +++ b/examples/lazy-source.js @@ -0,0 +1,26 @@ +goog.require('ol.Map'); +goog.require('ol.View'); +goog.require('ol.layer.Tile'); +goog.require('ol.source.MapQuest'); + +var source = new ol.source.MapQuest({layer: 'sat'}); + +var layer = new ol.layer.Tile(); + +var map = new ol.Map({ + layers: [layer], + renderer: exampleNS.getRendererFromQueryString(), + target: 'map', + view: new ol.View({ + center: [0, 0], + zoom: 2 + }) +}); + +document.getElementById('set-source').onclick = function() { + layer.setSource(source); +}; + +document.getElementById('unset-source').onclick = function() { + layer.setSource(null); +}; diff --git a/externs/olx.js b/externs/olx.js index 8669a9ad4d..57e5d11ac5 100644 --- a/externs/olx.js +++ b/externs/olx.js @@ -2508,7 +2508,7 @@ olx.layer.BaseOptions.prototype.maxResolution; * hue: (number|undefined), * opacity: (number|undefined), * saturation: (number|undefined), - * source: ol.source.Source, + * source: (ol.source.Source|undefined), * visible: (boolean|undefined), * extent: (ol.Extent|undefined), * minResolution: (number|undefined), @@ -2559,8 +2559,10 @@ olx.layer.LayerOptions.prototype.saturation; /** - * Source for this layer. - * @type {ol.source.Source} + * Source for this layer. If not provided to the constructor, the source can + * be set by calling {@link ol.layer.Layer#setSource layer.setSource(source)} + * after construction. + * @type {ol.source.Source|undefined} * @api stable */ olx.layer.LayerOptions.prototype.source; @@ -2710,7 +2712,7 @@ olx.layer.GroupOptions.prototype.layers; * maxResolution: (number|undefined), * opacity: (number|undefined), * saturation: (number|undefined), - * source: ol.source.Vector, + * source: (ol.source.Vector|undefined), * visible: (boolean|undefined)}} * @api */ @@ -2847,7 +2849,7 @@ olx.layer.HeatmapOptions.prototype.visible; * hue: (number|undefined), * opacity: (number|undefined), * saturation: (number|undefined), - * source: ol.source.Image, + * source: (ol.source.Image|undefined), * visible: (boolean|undefined), * extent: (ol.Extent|undefined), * minResolution: (number|undefined), @@ -2945,7 +2947,7 @@ olx.layer.ImageOptions.prototype.maxResolution; * opacity: (number|undefined), * preload: (number|undefined), * saturation: (number|undefined), - * source: ol.source.Tile, + * source: (ol.source.Tile|undefined), * visible: (boolean|undefined), * extent: (ol.Extent|undefined), * minResolution: (number|undefined), @@ -3063,7 +3065,7 @@ olx.layer.TileOptions.prototype.useInterimTilesOnError; * maxResolution: (number|undefined), * opacity: (number|undefined), * saturation: (number|undefined), - * source: ol.source.Vector, + * source: (ol.source.Vector|undefined), * style: (ol.style.Style|Array.|ol.style.StyleFunction|undefined), * visible: (boolean|undefined)}} * @api diff --git a/src/ol/control/attributioncontrol.js b/src/ol/control/attributioncontrol.js index 06914bf8c9..dcafa64c51 100644 --- a/src/ol/control/attributioncontrol.js +++ b/src/ol/control/attributioncontrol.js @@ -169,6 +169,9 @@ ol.control.Attribution.prototype.getSourceAttributions = function(frameState) { var hiddenAttributions = {}; for (i = 0, ii = layerStatesArray.length; i < ii; i++) { source = layerStatesArray[i].layer.getSource(); + if (goog.isNull(source)) { + continue; + } sourceKey = goog.getUid(source).toString(); sourceAttributions = source.getAttributions(); if (goog.isNull(sourceAttributions)) { diff --git a/src/ol/layer/imagelayer.js b/src/ol/layer/imagelayer.js index 29c668e799..f4b3a23cee 100644 --- a/src/ol/layer/imagelayer.js +++ b/src/ol/layer/imagelayer.js @@ -15,10 +15,11 @@ goog.require('ol.layer.Layer'); * @constructor * @extends {ol.layer.Layer} * @fires ol.render.Event - * @param {olx.layer.ImageOptions} options Layer options. + * @param {olx.layer.ImageOptions=} opt_options Layer options. * @api stable */ -ol.layer.Image = function(options) { +ol.layer.Image = function(opt_options) { + var options = goog.isDef(opt_options) ? opt_options : {}; goog.base(this, /** @type {olx.layer.LayerOptions} */ (options)); }; goog.inherits(ol.layer.Image, ol.layer.Layer); diff --git a/src/ol/layer/layer.js b/src/ol/layer/layer.js index 53c02a7db5..9415c8b05e 100644 --- a/src/ol/layer/layer.js +++ b/src/ol/layer/layer.js @@ -7,6 +7,7 @@ goog.require('goog.object'); goog.require('ol.Object'); goog.require('ol.layer.Base'); goog.require('ol.layer.LayerProperty'); +goog.require('ol.source.State'); @@ -42,7 +43,8 @@ ol.layer.Layer = function(options) { ol.Object.getChangeEventType(ol.layer.LayerProperty.SOURCE), this.handleSourcePropertyChange_, false, this); - this.setSource(options.source); + var source = goog.isDef(options.source) ? options.source : null; + this.setSource(source); }; goog.inherits(ol.layer.Layer, ol.layer.Base); @@ -83,7 +85,7 @@ ol.layer.Layer.prototype.getLayerStatesArray = function(opt_states) { /** * Get the layer source. - * @return {ol.source.Source} Source. + * @return {ol.source.Source} The layer source (or `null` if not yet set). * @observable * @api stable */ @@ -102,7 +104,14 @@ goog.exportProperty( * @inheritDoc */ ol.layer.Layer.prototype.getSourceState = function() { - return this.getSource().getState(); + var source = this.getSource(); + var state; + if (!goog.isNull(source)) { + state = source.getState(); + } else { + state = ol.source.State.UNDEFINED; + } + return state; }; diff --git a/src/ol/layer/tilelayer.js b/src/ol/layer/tilelayer.js index fac62872c9..9aa70cf5ab 100644 --- a/src/ol/layer/tilelayer.js +++ b/src/ol/layer/tilelayer.js @@ -24,10 +24,11 @@ ol.layer.TileProperty = { * @constructor * @extends {ol.layer.Layer} * @fires ol.render.Event - * @param {olx.layer.TileOptions} options Tile layer options. + * @param {olx.layer.TileOptions=} opt_options Tile layer options. * @api stable */ -ol.layer.Tile = function(options) { +ol.layer.Tile = function(opt_options) { + var options = goog.isDef(opt_options) ? opt_options : {}; goog.base(this, /** @type {olx.layer.LayerOptions} */ (options)); }; goog.inherits(ol.layer.Tile, ol.layer.Layer); diff --git a/src/ol/renderer/dom/domimagelayerrenderer.js b/src/ol/renderer/dom/domimagelayerrenderer.js index 04b4f16533..46242e14db 100644 --- a/src/ol/renderer/dom/domimagelayerrenderer.js +++ b/src/ol/renderer/dom/domimagelayerrenderer.js @@ -69,6 +69,15 @@ ol.renderer.dom.ImageLayer.prototype.forEachFeatureAtPixel = }; +/** + * @inheritDoc + */ +ol.renderer.dom.ImageLayer.prototype.clearFrame = function() { + goog.dom.removeChildren(this.target); + this.image_ = null; +}; + + /** * @inheritDoc */ diff --git a/src/ol/renderer/dom/domlayerrenderer.js b/src/ol/renderer/dom/domlayerrenderer.js index 8e582a6b4b..df94a74b2e 100644 --- a/src/ol/renderer/dom/domlayerrenderer.js +++ b/src/ol/renderer/dom/domlayerrenderer.js @@ -27,6 +27,12 @@ ol.renderer.dom.Layer = function(mapRenderer, layer, target) { goog.inherits(ol.renderer.dom.Layer, ol.renderer.Layer); +/** + * Clear rendered elements. + */ +ol.renderer.dom.Layer.prototype.clearFrame = goog.nullFunction; + + /** * @param {olx.FrameState} frameState Frame state. * @param {ol.layer.LayerState} layerState Layer state. diff --git a/src/ol/renderer/dom/dommaprenderer.js b/src/ol/renderer/dom/dommaprenderer.js index 3c8ff0c960..2cc2e64acf 100644 --- a/src/ol/renderer/dom/dommaprenderer.js +++ b/src/ol/renderer/dom/dommaprenderer.js @@ -244,6 +244,8 @@ ol.renderer.dom.Map.prototype.renderFrame = function(frameState) { if (layerRenderer.prepareFrame(frameState, layerState)) { layerRenderer.composeFrame(frameState, layerState); } + } else { + layerRenderer.clearFrame(); } } diff --git a/src/ol/renderer/dom/domtilelayerrenderer.js b/src/ol/renderer/dom/domtilelayerrenderer.js index fb294d0659..a2fdf6a44f 100644 --- a/src/ol/renderer/dom/domtilelayerrenderer.js +++ b/src/ol/renderer/dom/domtilelayerrenderer.js @@ -74,6 +74,15 @@ ol.renderer.dom.TileLayer = function(mapRenderer, tileLayer) { goog.inherits(ol.renderer.dom.TileLayer, ol.renderer.dom.Layer); +/** + * @inheritDoc + */ +ol.renderer.dom.TileLayer.prototype.clearFrame = function() { + goog.dom.removeChildren(this.target); + this.renderedRevision_ = 0; +}; + + /** * @inheritDoc */ diff --git a/src/ol/source/source.js b/src/ol/source/source.js index 776253f1f5..5cae7305ff 100644 --- a/src/ol/source/source.js +++ b/src/ol/source/source.js @@ -9,11 +9,12 @@ goog.require('ol.proj'); /** - * State of the source, one of 'loading', 'ready' or 'error'. + * State of the source, one of 'undefined', 'loading', 'ready' or 'error'. * @enum {string} * @api */ ol.source.State = { + UNDEFINED: 'undefined', LOADING: 'loading', READY: 'ready', ERROR: 'error'