diff --git a/examples/wmts-ign.js b/examples/wmts-ign.js index fbe36df153..46931fb3c9 100644 --- a/examples/wmts-ign.js +++ b/examples/wmts-ign.js @@ -1,4 +1,3 @@ -goog.require('ol.Attribution'); goog.require('ol.Map'); goog.require('ol.View'); goog.require('ol.control'); @@ -50,11 +49,9 @@ var ign_source = new ol.source.WMTS({ projection: 'EPSG:3857', tileGrid: tileGrid, style: 'normal', - attributions: [new ol.Attribution({ - html: '' + + attributions: '' + '' - })] }); var ign = new ol.layer.Tile({ diff --git a/examples/xyz-esri-4326-512.js b/examples/xyz-esri-4326-512.js index 2ec995853f..33b791200a 100644 --- a/examples/xyz-esri-4326-512.js +++ b/examples/xyz-esri-4326-512.js @@ -1,14 +1,9 @@ -goog.require('ol.Attribution'); goog.require('ol.Map'); goog.require('ol.View'); goog.require('ol.layer.Tile'); goog.require('ol.proj'); goog.require('ol.source.XYZ'); -var attribution = new ol.Attribution({ - html: 'Copyright:© 2013 ESRI, i-cubed, GeoEye' -}); - var projection = ol.proj.get('EPSG:4326'); // The tile size supported by the ArcGIS tile service. @@ -22,7 +17,7 @@ var map = new ol.Map({ layers: [ new ol.layer.Tile({ source: new ol.source.XYZ({ - attributions: [attribution], + attributions: 'Copyright:© 2013 ESRI, i-cubed, GeoEye', maxZoom: 16, projection: projection, tileSize: tileSize, diff --git a/examples/xyz-esri.js b/examples/xyz-esri.js index 8a3ed393c3..a1ad487259 100644 --- a/examples/xyz-esri.js +++ b/examples/xyz-esri.js @@ -1,4 +1,3 @@ -goog.require('ol.Attribution'); goog.require('ol.Map'); goog.require('ol.View'); goog.require('ol.layer.Tile'); @@ -6,17 +5,13 @@ goog.require('ol.proj'); goog.require('ol.source.XYZ'); -var attribution = new ol.Attribution({ - html: 'Tiles © ArcGIS' -}); - var map = new ol.Map({ target: 'map', layers: [ new ol.layer.Tile({ source: new ol.source.XYZ({ - attributions: [attribution], + attributions: 'Tiles © ArcGIS', url: 'https://server.arcgisonline.com/ArcGIS/rest/services/' + 'World_Topo_Map/MapServer/tile/{z}/{y}/{x}' }) diff --git a/externs/olx.js b/externs/olx.js index 3d93c0073c..f3b191b94a 100644 --- a/externs/olx.js +++ b/externs/olx.js @@ -5487,7 +5487,7 @@ olx.source.OSMOptions.prototype.wrapX; /** - * @typedef {{attributions: (Array.|undefined), + * @typedef {{attributions: (ol.AttributionLike|undefined), * crossOrigin: (null|string|undefined), * hidpi: (boolean|undefined), * logo: (string|olx.LogoOptions|undefined), @@ -5503,7 +5503,7 @@ olx.source.ImageArcGISRestOptions; /** * Attributions. - * @type {Array.|undefined} + * @type {ol.AttributionLike|undefined} * @api */ olx.source.ImageArcGISRestOptions.prototype.attributions; @@ -8329,7 +8329,6 @@ olx.view.FitOptions.prototype.callback; /** * @typedef {{animate: boolean, - * attributions: Object., * coordinateToPixelTransform: ol.Transform, * extent: (null|ol.Extent), * focus: ol.Coordinate, @@ -8377,7 +8376,8 @@ olx.FrameState.prototype.viewState; * @typedef {{center: ol.Coordinate, * projection: ol.proj.Projection, * resolution: number, - * rotation: number}} + * rotation: number, + * zoom: number}} */ olx.ViewState; @@ -8410,6 +8410,14 @@ olx.ViewState.prototype.resolution; olx.ViewState.prototype.rotation; +/** + * The current zoom level. + * @type {number} + * @api + */ +olx.ViewState.prototype.zoom; + + /** * @typedef {{initialSize: (number|undefined), * maxSize: (number|undefined), diff --git a/src/ol/control/attribution.js b/src/ol/control/attribution.js index 7054582659..e6399b8eb7 100644 --- a/src/ol/control/attribution.js +++ b/src/ol/control/attribution.js @@ -3,11 +3,13 @@ goog.provide('ol.control.Attribution'); goog.require('ol'); -goog.require('ol.dom'); +goog.require('ol.array'); goog.require('ol.control.Control'); goog.require('ol.css'); +goog.require('ol.dom'); goog.require('ol.events'); goog.require('ol.events.EventType'); +goog.require('ol.layer.Layer'); goog.require('ol.obj'); @@ -116,24 +118,19 @@ ol.control.Attribution = function(opt_options) { target: options.target }); + /** + * A list of currently rendered resolutions. + * @type {Array.} + * @private + */ + this.renderedAttributions_ = []; + /** * @private * @type {boolean} */ this.renderedVisible_ = true; - /** - * @private - * @type {Object.} - */ - this.attributionElements_ = {}; - - /** - * @private - * @type {Object.} - */ - this.attributionElementRenderedVisible_ = {}; - /** * @private * @type {Object.} @@ -145,59 +142,62 @@ ol.inherits(ol.control.Attribution, ol.control.Control); /** - * @param {?olx.FrameState} frameState Frame state. - * @return {Array.>} Attributions. + * Get a list of visible attributions. + * @param {olx.FrameState} frameState Frame state. + * @return {Array.} Attributions. + * @private */ -ol.control.Attribution.prototype.getSourceAttributions = function(frameState) { - var i, ii, j, jj, tileRanges, source, sourceAttribution, - sourceAttributionKey, sourceAttributions, sourceKey; - var intersectsTileRange; +ol.control.Attribution.prototype.getSourceAttributions_ = function(frameState) { + /** + * Used to determine if an attribution already exists. + * @type {Object.} + */ + var lookup = {}; + + /** + * A list of visible attributions. + * @type {Array.} + */ + var visibleAttributions = []; + var layerStatesArray = frameState.layerStatesArray; - /** @type {Object.} */ - var attributions = ol.obj.assign({}, frameState.attributions); - /** @type {Object.} */ - var hiddenAttributions = {}; - var uniqueAttributions = {}; - var projection = /** @type {!ol.proj.Projection} */ (frameState.viewState.projection); - for (i = 0, ii = layerStatesArray.length; i < ii; i++) { - source = layerStatesArray[i].layer.getSource(); + var resolution = frameState.viewState.resolution; + for (var i = 0, ii = layerStatesArray.length; i < ii; ++i) { + var layerState = layerStatesArray[i]; + if (!ol.layer.Layer.visibleAtResolution(layerState, resolution)) { + continue; + } + + var source = layerState.layer.getSource(); if (!source) { continue; } - sourceKey = ol.getUid(source).toString(); - sourceAttributions = source.getAttributions(); - if (!sourceAttributions) { + + var attributionGetter = source.getAttributions2(); + if (!attributionGetter) { continue; } - for (j = 0, jj = sourceAttributions.length; j < jj; j++) { - sourceAttribution = sourceAttributions[j]; - sourceAttributionKey = ol.getUid(sourceAttribution).toString(); - if (sourceAttributionKey in attributions) { - continue; - } - tileRanges = frameState.usedTiles[sourceKey]; - if (tileRanges) { - var tileGrid = /** @type {ol.source.Tile} */ (source).getTileGridForProjection(projection); - intersectsTileRange = sourceAttribution.intersectsAnyTileRange( - tileRanges, tileGrid, projection); - } else { - intersectsTileRange = false; - } - if (intersectsTileRange) { - if (sourceAttributionKey in hiddenAttributions) { - delete hiddenAttributions[sourceAttributionKey]; + + var attributions = attributionGetter(frameState); + if (!attributions) { + continue; + } + + if (Array.isArray(attributions)) { + for (var j = 0, jj = attributions.length; j < jj; ++j) { + if (!(attributions[j] in lookup)) { + visibleAttributions.push(attributions[j]); + lookup[attributions[j]] = true; } - var html = sourceAttribution.getHTML(); - if (!(html in uniqueAttributions)) { - uniqueAttributions[html] = true; - attributions[sourceAttributionKey] = sourceAttribution; - } - } else { - hiddenAttributions[sourceAttributionKey] = sourceAttribution; + } + } else { + if (!(attributions in lookup)) { + visibleAttributions.push(attributions); + lookup[attributions] = true; } } } - return [attributions, hiddenAttributions]; + return visibleAttributions; }; @@ -217,7 +217,6 @@ ol.control.Attribution.render = function(mapEvent) { * @param {?olx.FrameState} frameState Frame state. */ ol.control.Attribution.prototype.updateElement_ = function(frameState) { - if (!frameState) { if (this.renderedVisible_) { this.element.style.display = 'none'; @@ -226,65 +225,38 @@ ol.control.Attribution.prototype.updateElement_ = function(frameState) { return; } - var attributions = this.getSourceAttributions(frameState); - /** @type {Object.} */ - var visibleAttributions = attributions[0]; - /** @type {Object.} */ - var hiddenAttributions = attributions[1]; - - var attributionElement, attributionKey; - for (attributionKey in this.attributionElements_) { - if (attributionKey in visibleAttributions) { - if (!this.attributionElementRenderedVisible_[attributionKey]) { - this.attributionElements_[attributionKey].style.display = ''; - this.attributionElementRenderedVisible_[attributionKey] = true; - } - delete visibleAttributions[attributionKey]; - } else if (attributionKey in hiddenAttributions) { - if (this.attributionElementRenderedVisible_[attributionKey]) { - this.attributionElements_[attributionKey].style.display = 'none'; - delete this.attributionElementRenderedVisible_[attributionKey]; - } - delete hiddenAttributions[attributionKey]; - } else { - ol.dom.removeNode(this.attributionElements_[attributionKey]); - delete this.attributionElements_[attributionKey]; - delete this.attributionElementRenderedVisible_[attributionKey]; - } - } - for (attributionKey in visibleAttributions) { - attributionElement = document.createElement('LI'); - attributionElement.innerHTML = - visibleAttributions[attributionKey].getHTML(); - this.ulElement_.appendChild(attributionElement); - this.attributionElements_[attributionKey] = attributionElement; - this.attributionElementRenderedVisible_[attributionKey] = true; - } - for (attributionKey in hiddenAttributions) { - attributionElement = document.createElement('LI'); - attributionElement.innerHTML = - hiddenAttributions[attributionKey].getHTML(); - attributionElement.style.display = 'none'; - this.ulElement_.appendChild(attributionElement); - this.attributionElements_[attributionKey] = attributionElement; + var attributions = this.getSourceAttributions_(frameState); + if (ol.array.equals(attributions, this.renderedAttributions_)) { + return; } - var renderVisible = - !ol.obj.isEmpty(this.attributionElementRenderedVisible_) || - !ol.obj.isEmpty(frameState.logos); - if (this.renderedVisible_ != renderVisible) { - this.element.style.display = renderVisible ? '' : 'none'; - this.renderedVisible_ = renderVisible; + // remove everything but the logo + while (this.ulElement_.lastChild !== this.logoLi_) { + this.ulElement_.removeChild(this.ulElement_.lastChild); } - if (renderVisible && - ol.obj.isEmpty(this.attributionElementRenderedVisible_)) { + + // append the attributions + for (var i = 0, ii = attributions.length; i < ii; ++i) { + var element = document.createElement('LI'); + element.innerHTML = attributions[i]; + this.ulElement_.appendChild(element); + } + + + if (attributions.length === 0 && this.renderedAttributions_.length > 0) { this.element.classList.add('ol-logo-only'); - } else { + } else if (this.renderedAttributions_.length === 0 && attributions.length > 0) { this.element.classList.remove('ol-logo-only'); } - this.insertLogos_(frameState); + var visible = attributions.length > 0 || !ol.obj.isEmpty(frameState.logos); + if (this.renderedVisible_ != visible) { + this.element.style.display = visible ? '' : 'none'; + this.renderedVisible_ = visible; + } + this.renderedAttributions_ = attributions; + this.insertLogos_(frameState); }; diff --git a/src/ol/image.js b/src/ol/image.js index 222e78398a..4c49259f1e 100644 --- a/src/ol/image.js +++ b/src/ol/image.js @@ -14,16 +14,13 @@ goog.require('ol.extent'); * @param {ol.Extent} extent Extent. * @param {number|undefined} resolution Resolution. * @param {number} pixelRatio Pixel ratio. - * @param {Array.} attributions Attributions. * @param {string} src Image source URI. * @param {?string} crossOrigin Cross origin. * @param {ol.ImageLoadFunctionType} imageLoadFunction Image load function. */ -ol.Image = function(extent, resolution, pixelRatio, attributions, src, - crossOrigin, imageLoadFunction) { +ol.Image = function(extent, resolution, pixelRatio, src, crossOrigin, imageLoadFunction) { - ol.ImageBase.call(this, extent, resolution, pixelRatio, ol.ImageState.IDLE, - attributions); + ol.ImageBase.call(this, extent, resolution, pixelRatio, ol.ImageState.IDLE); /** * @private diff --git a/src/ol/imagebase.js b/src/ol/imagebase.js index 867d321107..7ce6e3bcb1 100644 --- a/src/ol/imagebase.js +++ b/src/ol/imagebase.js @@ -13,18 +13,11 @@ goog.require('ol.events.EventType'); * @param {number|undefined} resolution Resolution. * @param {number} pixelRatio Pixel ratio. * @param {ol.ImageState} state State. - * @param {Array.} attributions Attributions. */ -ol.ImageBase = function(extent, resolution, pixelRatio, state, attributions) { +ol.ImageBase = function(extent, resolution, pixelRatio, state) { ol.events.EventTarget.call(this); - /** - * @private - * @type {Array.} - */ - this.attributions_ = attributions; - /** * @protected * @type {ol.Extent} @@ -61,14 +54,6 @@ ol.ImageBase.prototype.changed = function() { }; -/** - * @return {Array.} Attributions. - */ -ol.ImageBase.prototype.getAttributions = function() { - return this.attributions_; -}; - - /** * @return {ol.Extent} Extent. */ diff --git a/src/ol/imagecanvas.js b/src/ol/imagecanvas.js index 748ac14e3d..23aff4a0ca 100644 --- a/src/ol/imagecanvas.js +++ b/src/ol/imagecanvas.js @@ -11,13 +11,11 @@ goog.require('ol.ImageState'); * @param {ol.Extent} extent Extent. * @param {number} resolution Resolution. * @param {number} pixelRatio Pixel ratio. - * @param {Array.} attributions Attributions. * @param {HTMLCanvasElement} canvas Canvas. * @param {ol.ImageCanvasLoader=} opt_loader Optional loader function to * support asynchronous canvas drawing. */ -ol.ImageCanvas = function(extent, resolution, pixelRatio, attributions, - canvas, opt_loader) { +ol.ImageCanvas = function(extent, resolution, pixelRatio, canvas, opt_loader) { /** * Optional canvas loader function. @@ -29,7 +27,7 @@ ol.ImageCanvas = function(extent, resolution, pixelRatio, attributions, var state = opt_loader !== undefined ? ol.ImageState.IDLE : ol.ImageState.LOADED; - ol.ImageBase.call(this, extent, resolution, pixelRatio, state, attributions); + ol.ImageBase.call(this, extent, resolution, pixelRatio, state); /** * @private diff --git a/src/ol/renderer/canvas/imagelayer.js b/src/ol/renderer/canvas/imagelayer.js index 878846926c..c38b73d3d1 100644 --- a/src/ol/renderer/canvas/imagelayer.js +++ b/src/ol/renderer/canvas/imagelayer.js @@ -134,7 +134,6 @@ ol.renderer.canvas.ImageLayer.prototype.prepareFrame = function(frameState, laye 0, -viewCenter[0], -viewCenter[1]); - this.updateAttributions(frameState.attributions, image.getAttributions()); this.updateLogos(frameState, imageSource); this.renderedResolution = imageResolution * pixelRatio / imagePixelRatio; } diff --git a/src/ol/renderer/canvas/vectorlayer.js b/src/ol/renderer/canvas/vectorlayer.js index b900bf2131..a50421a5f0 100644 --- a/src/ol/renderer/canvas/vectorlayer.js +++ b/src/ol/renderer/canvas/vectorlayer.js @@ -261,8 +261,6 @@ ol.renderer.canvas.VectorLayer.prototype.prepareFrame = function(frameState, lay var vectorLayer = /** @type {ol.layer.Vector} */ (this.getLayer()); var vectorSource = vectorLayer.getSource(); - this.updateAttributions( - frameState.attributions, vectorSource.getAttributions()); this.updateLogos(frameState, vectorSource); var animating = frameState.viewHints[ol.ViewHint.ANIMATING]; diff --git a/src/ol/renderer/layer.js b/src/ol/renderer/layer.js index f966d5c978..f135b5d620 100644 --- a/src/ol/renderer/layer.js +++ b/src/ol/renderer/layer.js @@ -165,23 +165,6 @@ ol.renderer.Layer.prototype.scheduleExpireCache = function(frameState, tileSourc }; -/** - * @param {Object.} attributionsSet Attributions - * set (target). - * @param {Array.} attributions Attributions (source). - * @protected - */ -ol.renderer.Layer.prototype.updateAttributions = function(attributionsSet, attributions) { - if (attributions) { - var attribution, i, ii; - for (i = 0, ii = attributions.length; i < ii; ++i) { - attribution = attributions[i]; - attributionsSet[ol.getUid(attribution).toString()] = attribution; - } - } -}; - - /** * @param {olx.FrameState} frameState Frame state. * @param {ol.source.Source} source Source. diff --git a/src/ol/renderer/webgl/imagelayer.js b/src/ol/renderer/webgl/imagelayer.js index ba81204703..485b442eb7 100644 --- a/src/ol/renderer/webgl/imagelayer.js +++ b/src/ol/renderer/webgl/imagelayer.js @@ -190,7 +190,6 @@ ol.renderer.webgl.ImageLayer.prototype.prepareFrame = function(frameState, layer this.image_ = image; this.texture = texture; - this.updateAttributions(frameState.attributions, image.getAttributions()); this.updateLogos(frameState, imageSource); } diff --git a/src/ol/renderer/webgl/vectorlayer.js b/src/ol/renderer/webgl/vectorlayer.js index b34425e950..d6ee33b21d 100644 --- a/src/ol/renderer/webgl/vectorlayer.js +++ b/src/ol/renderer/webgl/vectorlayer.js @@ -215,8 +215,6 @@ ol.renderer.webgl.VectorLayer.prototype.prepareFrame = function(frameState, laye var vectorLayer = /** @type {ol.layer.Vector} */ (this.getLayer()); var vectorSource = vectorLayer.getSource(); - this.updateAttributions( - frameState.attributions, vectorSource.getAttributions()); this.updateLogos(frameState, vectorSource); var animating = frameState.viewHints[ol.ViewHint.ANIMATING]; diff --git a/src/ol/reproj/image.js b/src/ol/reproj/image.js index 4dccd442b5..fa3a921e01 100644 --- a/src/ol/reproj/image.js +++ b/src/ol/reproj/image.js @@ -100,15 +100,12 @@ ol.reproj.Image = function(sourceProj, targetProj, var state = ol.ImageState.LOADED; - var attributions = []; if (this.sourceImage_) { state = ol.ImageState.IDLE; - attributions = this.sourceImage_.getAttributions(); } - ol.ImageBase.call(this, targetExtent, targetResolution, this.sourcePixelRatio_, - state, attributions); + ol.ImageBase.call(this, targetExtent, targetResolution, this.sourcePixelRatio_, state); }; ol.inherits(ol.reproj.Image, ol.ImageBase); diff --git a/src/ol/source/bingmaps.js b/src/ol/source/bingmaps.js index 1c340a15ad..63ea203779 100644 --- a/src/ol/source/bingmaps.js +++ b/src/ol/source/bingmaps.js @@ -1,7 +1,6 @@ goog.provide('ol.source.BingMaps'); goog.require('ol'); -goog.require('ol.Attribution'); goog.require('ol.TileUrlFunction'); goog.require('ol.extent'); goog.require('ol.net'); @@ -81,14 +80,12 @@ ol.inherits(ol.source.BingMaps, ol.source.TileImage); * The attribution containing a link to the Microsoft® Bing™ Maps Platform APIs’ * Terms Of Use. * @const - * @type {ol.Attribution} + * @type {string} * @api */ -ol.source.BingMaps.TOS_ATTRIBUTION = new ol.Attribution({ - html: '' + - 'Terms of Use' -}); + 'Terms of Use'; /** @@ -181,31 +178,32 @@ ol.source.BingMaps.prototype.handleImageryMetadataResponse = function(response) var transform = ol.proj.getTransformFromProjections( ol.proj.get('EPSG:4326'), this.getProjection()); - var attributions = resource.imageryProviders.map(function(imageryProvider) { - var html = imageryProvider.attribution; - /** @type {Object.>} */ - var tileRanges = {}; - imageryProvider.coverageAreas.forEach(function(coverageArea) { - var minZ = coverageArea.zoomMin; - var maxZ = Math.min(coverageArea.zoomMax, maxZoom); - var bbox = coverageArea.bbox; - var epsg4326Extent = [bbox[1], bbox[0], bbox[3], bbox[2]]; - var extent = ol.extent.applyTransform(epsg4326Extent, transform); - var tileRange, z, zKey; - for (z = minZ; z <= maxZ; ++z) { - zKey = z.toString(); - tileRange = tileGrid.getTileRangeForExtentAndZ(extent, z); - if (zKey in tileRanges) { - tileRanges[zKey].push(tileRange); - } else { - tileRanges[zKey] = [tileRange]; + this.setAttributions(function(frameState) { + var attributions = []; + var zoom = frameState.viewState.zoom; + resource.imageryProviders.map(function(imageryProvider) { + var intersects = false; + var coverageAreas = imageryProvider.coverageAreas; + for (var i = 0, ii = coverageAreas.length; i < ii; ++i) { + var coverageArea = coverageAreas[i]; + if (zoom >= coverageArea.zoomMin && zoom <= coverageArea.zoomMax) { + var bbox = coverageArea.bbox; + var epsg4326Extent = [bbox[1], bbox[0], bbox[3], bbox[2]]; + var extent = ol.extent.applyTransform(epsg4326Extent, transform); + if (ol.extent.intersects(extent, frameState.extent)) { + intersects = true; + break; + } } } + if (intersects) { + attributions.push(imageryProvider.attribution); + } }); - return new ol.Attribution({html: html, tileRanges: tileRanges}); + + attributions.push(ol.source.BingMaps.TOS_ATTRIBUTION); + return attributions; }); - attributions.push(ol.source.BingMaps.TOS_ATTRIBUTION); - this.setAttributions(attributions); } this.setLogo(brandLogoUri); diff --git a/src/ol/source/imagearcgisrest.js b/src/ol/source/imagearcgisrest.js index 00839398fb..07bea9743f 100644 --- a/src/ol/source/imagearcgisrest.js +++ b/src/ol/source/imagearcgisrest.js @@ -169,7 +169,7 @@ ol.source.ImageArcGISRest.prototype.getImageInternal = function(extent, resoluti projection, params); this.image_ = new ol.Image(extent, resolution, pixelRatio, - this.getAttributions(), url, this.crossOrigin_, this.imageLoadFunction_); + url, this.crossOrigin_, this.imageLoadFunction_); this.renderedRevision_ = this.getRevision(); diff --git a/src/ol/source/imagecanvas.js b/src/ol/source/imagecanvas.js index 8dfe02c84a..2b622dc0e0 100644 --- a/src/ol/source/imagecanvas.js +++ b/src/ol/source/imagecanvas.js @@ -78,8 +78,7 @@ ol.source.ImageCanvas.prototype.getImageInternal = function(extent, resolution, var canvasElement = this.canvasFunction_( extent, resolution, pixelRatio, size, projection); if (canvasElement) { - canvas = new ol.ImageCanvas(extent, resolution, pixelRatio, - this.getAttributions(), canvasElement); + canvas = new ol.ImageCanvas(extent, resolution, pixelRatio, canvasElement); } this.canvas_ = canvas; this.renderedRevision_ = this.getRevision(); diff --git a/src/ol/source/imagemapguide.js b/src/ol/source/imagemapguide.js index d66bb3eec9..605fd1150b 100644 --- a/src/ol/source/imagemapguide.js +++ b/src/ol/source/imagemapguide.js @@ -141,7 +141,7 @@ ol.source.ImageMapGuide.prototype.getImageInternal = function(extent, resolution var imageUrl = this.getUrl(this.url_, this.params_, extent, size, projection); image = new ol.Image(extent, resolution, pixelRatio, - this.getAttributions(), imageUrl, this.crossOrigin_, + imageUrl, this.crossOrigin_, this.imageLoadFunction_); ol.events.listen(image, ol.events.EventType.CHANGE, this.handleImageChange, this); diff --git a/src/ol/source/imagestatic.js b/src/ol/source/imagestatic.js index 4c7c063299..888ee26218 100644 --- a/src/ol/source/imagestatic.js +++ b/src/ol/source/imagestatic.js @@ -40,8 +40,7 @@ ol.source.ImageStatic = function(options) { * @private * @type {ol.Image} */ - this.image_ = new ol.Image(imageExtent, undefined, 1, this.getAttributions(), - options.url, crossOrigin, imageLoadFunction); + this.image_ = new ol.Image(imageExtent, undefined, 1, options.url, crossOrigin, imageLoadFunction); /** * @private diff --git a/src/ol/source/imagewms.js b/src/ol/source/imagewms.js index 12fbb4c04b..c44511b017 100644 --- a/src/ol/source/imagewms.js +++ b/src/ol/source/imagewms.js @@ -225,7 +225,7 @@ ol.source.ImageWMS.prototype.getImageInternal = function(extent, resolution, pix projection, params); this.image_ = new ol.Image(requestExtent, resolution, pixelRatio, - this.getAttributions(), url, this.crossOrigin_, this.imageLoadFunction_); + url, this.crossOrigin_, this.imageLoadFunction_); this.renderedRevision_ = this.getRevision(); diff --git a/src/ol/source/osm.js b/src/ol/source/osm.js index 11eebc5df6..5ae100c639 100644 --- a/src/ol/source/osm.js +++ b/src/ol/source/osm.js @@ -1,7 +1,6 @@ goog.provide('ol.source.OSM'); goog.require('ol'); -goog.require('ol.Attribution'); goog.require('ol.source.XYZ'); @@ -51,11 +50,9 @@ ol.inherits(ol.source.OSM, ol.source.XYZ); * The attribution containing a link to the OpenStreetMap Copyright and License * page. * @const - * @type {ol.Attribution} + * @type {string} * @api */ -ol.source.OSM.ATTRIBUTION = new ol.Attribution({ - html: '© ' + +ol.source.OSM.ATTRIBUTION = '© ' + 'OpenStreetMap ' + - 'contributors.' -}); + 'contributors.'; diff --git a/src/ol/source/raster.js b/src/ol/source/raster.js index 3588573adb..fd546b3746 100644 --- a/src/ol/source/raster.js +++ b/src/ol/source/raster.js @@ -292,8 +292,7 @@ ol.source.Raster.prototype.onWorkerComplete_ = function(frameState, err, output, var width = Math.round(ol.extent.getWidth(extent) / resolution); var height = Math.round(ol.extent.getHeight(extent) / resolution); context = ol.dom.createCanvasContext2D(width, height); - this.renderedImageCanvas_ = new ol.ImageCanvas( - extent, resolution, 1, this.getAttributions(), context.canvas); + this.renderedImageCanvas_ = new ol.ImageCanvas(extent, resolution, 1, context.canvas); } context.putImageData(output, 0, 0); diff --git a/src/ol/source/source.js b/src/ol/source/source.js index 82b1c98447..2f37960fd3 100644 --- a/src/ol/source/source.js +++ b/src/ol/source/source.js @@ -35,7 +35,13 @@ ol.source.Source = function(options) { * @private * @type {Array.} */ - this.attributions_ = ol.source.Source.toAttributionsArray_(options.attributions); + this.attributions_ = null; + + /** + * @private + * @type {?ol.Attribution2} + */ + this.attributions2_ = this.adaptAttributions_(options.attributions); /** * @private @@ -60,36 +66,60 @@ ol.source.Source = function(options) { ol.inherits(ol.source.Source, ol.Object); /** - * Turns various ways of defining an attribution to an array of `ol.Attributions`. - * - * @param {ol.AttributionLike|undefined} - * attributionLike The attributions as string, array of strings, - * `ol.Attribution`, array of `ol.Attribution` or undefined. - * @return {Array.} The array of `ol.Attribution` or null if - * `undefined` was given. + * Turns the attributions option into an attributions function. + * @param {ol.AttributionLike|undefined} attributionLike The attribution option. + * @return {?ol.Attribution2} An attribution function (or null). */ -ol.source.Source.toAttributionsArray_ = function(attributionLike) { - if (typeof attributionLike === 'string') { - return [new ol.Attribution({html: attributionLike})]; - } else if (attributionLike instanceof ol.Attribution) { - return [attributionLike]; - } else if (Array.isArray(attributionLike)) { - var len = attributionLike.length; - var attributions = new Array(len); - for (var i = 0; i < len; i++) { - var item = attributionLike[i]; - if (typeof item === 'string') { - attributions[i] = new ol.Attribution({html: item}); - } else { - attributions[i] = item; - } - } - return attributions; - } else { +ol.source.Source.prototype.adaptAttributions_ = function(attributionLike) { + if (!attributionLike) { return null; } -}; + if (attributionLike instanceof ol.Attribution) { + // TODO: remove attributions_ in next major release + this.attributions_ = [attributionLike]; + + return function(frameState) { + return [attributionLike.getHTML()]; + }; + } + if (Array.isArray(attributionLike)) { + if (attributionLike[0] instanceof ol.Attribution) { + + // TODO: remove attributions_ in next major release + this.attributions_ = attributionLike; + + var attributions = attributionLike.map(function(attribution) { + return attribution.getHTML(); + }); + return function(frameState) { + return attributions; + }; + } + + // TODO: remove attributions_ in next major release + this.attributions_ = attributionLike.map(function(attribution) { + return new ol.Attribution({html: attribution}); + }); + + return function(frameState) { + return attributionLike; + }; + } + + if (typeof attributionLike === 'function') { + return attributionLike; + } + + // TODO: remove attributions_ in next major release + this.attributions_ = [ + new ol.Attribution({html: attributionLike}) + ]; + + return function(frameState) { + return [attributionLike]; + }; +}; /** * @param {ol.Coordinate} coordinate Coordinate. @@ -115,6 +145,15 @@ ol.source.Source.prototype.getAttributions = function() { }; +/** + * Get the attribution function for the source. + * @return {?ol.Attribution2} Attribution function. + */ +ol.source.Source.prototype.getAttributions2 = function() { + return this.attributions2_; +}; + + /** * Get the logo of the source. * @return {string|olx.LogoOptions|undefined} Logo. @@ -172,12 +211,12 @@ ol.source.Source.prototype.refresh = function() { /** * Set the attributions of the source. * @param {ol.AttributionLike|undefined} attributions Attributions. - * Can be passed as `string`, `Array`, `{@link ol.Attribution}`, - * `Array<{@link ol.Attribution}>` or `undefined`. + * Can be passed as `string`, `Array`, `{@link ol.Attribution2}`, + * or `undefined`. * @api */ ol.source.Source.prototype.setAttributions = function(attributions) { - this.attributions_ = ol.source.Source.toAttributionsArray_(attributions); + this.attributions2_ = this.adaptAttributions_(attributions); this.changed(); }; diff --git a/src/ol/source/stamen.js b/src/ol/source/stamen.js index 1beca37cd1..673f5a7304 100644 --- a/src/ol/source/stamen.js +++ b/src/ol/source/stamen.js @@ -1,7 +1,6 @@ goog.provide('ol.source.Stamen'); goog.require('ol'); -goog.require('ol.Attribution'); goog.require('ol.source.OSM'); goog.require('ol.source.XYZ'); @@ -44,14 +43,12 @@ ol.inherits(ol.source.Stamen, ol.source.XYZ); /** * @const - * @type {Array.} + * @type {Array.} */ ol.source.Stamen.ATTRIBUTIONS = [ - new ol.Attribution({ - html: 'Map tiles by Stamen Design, ' + + 'Map tiles by Stamen Design, ' + 'under CC BY' + - ' 3.0.' - }), + ' 3.0.', ol.source.OSM.ATTRIBUTION ]; diff --git a/src/ol/source/tilejson.js b/src/ol/source/tilejson.js index d3ab7cb923..e186c8b579 100644 --- a/src/ol/source/tilejson.js +++ b/src/ol/source/tilejson.js @@ -7,7 +7,6 @@ goog.provide('ol.source.TileJSON'); goog.require('ol'); -goog.require('ol.Attribution'); goog.require('ol.TileUrlFunction'); goog.require('ol.asserts'); goog.require('ol.extent'); @@ -136,23 +135,17 @@ ol.source.TileJSON.prototype.handleTileJSONResponse = function(tileJSON) { this.tileUrlFunction = ol.TileUrlFunction.createFromTemplates(tileJSON.tiles, tileGrid); - if (tileJSON.attribution !== undefined && !this.getAttributions()) { + if (tileJSON.attribution !== undefined && !this.getAttributions2()) { var attributionExtent = extent !== undefined ? extent : epsg4326Projection.getExtent(); - /** @type {Object.>} */ - var tileRanges = {}; - var z, zKey; - for (z = minZoom; z <= maxZoom; ++z) { - zKey = z.toString(); - tileRanges[zKey] = - [tileGrid.getTileRangeForExtentAndZ(attributionExtent, z)]; - } - this.setAttributions([ - new ol.Attribution({ - html: tileJSON.attribution, - tileRanges: tileRanges - }) - ]); + + this.setAttributions(function(frameState) { + if (ol.extent.intersects(attributionExtent, frameState.extent)) { + return [tileJSON.attribution]; + } + return null; + }); + } this.tileJSON_ = tileJSON; this.setState(ol.source.State.READY); diff --git a/src/ol/source/tileutfgrid.js b/src/ol/source/tileutfgrid.js index 023fffe843..50249b6d79 100644 --- a/src/ol/source/tileutfgrid.js +++ b/src/ol/source/tileutfgrid.js @@ -1,7 +1,6 @@ goog.provide('ol.source.TileUTFGrid'); goog.require('ol'); -goog.require('ol.Attribution'); goog.require('ol.Tile'); goog.require('ol.TileState'); goog.require('ol.TileUrlFunction'); @@ -198,20 +197,13 @@ ol.source.TileUTFGrid.prototype.handleTileJSONResponse = function(tileJSON) { if (tileJSON.attribution !== undefined) { var attributionExtent = extent !== undefined ? extent : epsg4326Projection.getExtent(); - /** @type {Object.>} */ - var tileRanges = {}; - var z, zKey; - for (z = minZoom; z <= maxZoom; ++z) { - zKey = z.toString(); - tileRanges[zKey] = - [tileGrid.getTileRangeForExtentAndZ(attributionExtent, z)]; - } - this.setAttributions([ - new ol.Attribution({ - html: tileJSON.attribution, - tileRanges: tileRanges - }) - ]); + + this.setAttributions(function(frameState) { + if (ol.extent.intersects(attributionExtent, frameState.extent)) { + return [tileJSON.attribution]; + } + return null; + }); } this.setState(ol.source.State.READY); diff --git a/src/ol/typedefs.js b/src/ol/typedefs.js index 2781cb321a..8d6c599715 100644 --- a/src/ol/typedefs.js +++ b/src/ol/typedefs.js @@ -46,15 +46,25 @@ ol.AtlasManagerInfo; * A type that can be used to provide attribution information for data sources. * * It represents either - * * a simple string (e.g. `'© Acme Inc.'`), - * * an array of simple strings (e.g. `['© Acme Inc.', '© Bacme Inc.']`), - * * an instance of `{@link ol.Attribution}`, - * * or an array with multiple `{@link ol.Attribution}` instances. - * @typedef {string|Array.|ol.Attribution|Array.} + * * a simple string (e.g. `'© Acme Inc.'`) + * * an array of simple strings (e.g. `['© Acme Inc.', '© Bacme Inc.']`) + * * a function that returns a string or array of strings (`{@link ol.Attribution2}`) + * + * Note that the `{@link ol.Attribution}` constructor is deprecated. + * @typedef {string|Array.|ol.Attribution2|ol.Attribution|Array.} */ ol.AttributionLike; +/** + * A function that returns a string or an array of strings representing source + * attributions. + * + * @typedef {function(olx.FrameState): (string|Array.)} + */ +ol.Attribution2; + + /** * @typedef {{fillStyle: ol.ColorLike}} */ diff --git a/src/ol/view.js b/src/ol/view.js index d85262078b..ddfefe4a35 100644 --- a/src/ol/view.js +++ b/src/ol/view.js @@ -781,7 +781,8 @@ ol.View.prototype.getState = function() { center: center.slice(), projection: projection !== undefined ? projection : null, resolution: resolution, - rotation: rotation + rotation: rotation, + zoom: this.getZoom() }); }; diff --git a/test/spec/ol/renderer/layer.test.js b/test/spec/ol/renderer/layer.test.js index 01bb6ac78e..32c7379644 100644 --- a/test/spec/ol/renderer/layer.test.js +++ b/test/spec/ol/renderer/layer.test.js @@ -22,12 +22,10 @@ describe('ol.renderer.Layer', function() { var extent = []; var resolution = 1; var pixelRatio = 1; - var attributions = []; var src = ''; var crossOrigin = ''; imageLoadFunction = sinon.spy(); - image = new ol.Image(extent, resolution, pixelRatio, attributions, - src, crossOrigin, imageLoadFunction); + image = new ol.Image(extent, resolution, pixelRatio, src, crossOrigin, imageLoadFunction); }); describe('load IDLE image', function() { diff --git a/test/spec/ol/reproj/image.test.js b/test/spec/ol/reproj/image.test.js index c2c87990c7..9467a5fcfa 100644 --- a/test/spec/ol/reproj/image.test.js +++ b/test/spec/ol/reproj/image.test.js @@ -12,7 +12,7 @@ describe('ol.reproj.Image', function() { ol.proj.get('EPSG:3857'), ol.proj.get('EPSG:4326'), [-180, -85, 180, 85], 10, pixelRatio, function(extent, resolution, pixelRatio) { - return new ol.Image(extent, resolution, pixelRatio, [], + return new ol.Image(extent, resolution, pixelRatio, 'data:image/gif;base64,' + 'R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=', null, function(image, src) { diff --git a/test/spec/ol/source/source.test.js b/test/spec/ol/source/source.test.js index 01ca6b6620..acebf4ff52 100644 --- a/test/spec/ol/source/source.test.js +++ b/test/spec/ol/source/source.test.js @@ -1,5 +1,3 @@ - - goog.require('ol.Attribution'); goog.require('ol.proj'); goog.require('ol.source.Source'); @@ -19,68 +17,52 @@ describe('ol.source.Source', function() { describe('config option `attributions`', function() { it('accepts undefined', function() { var source = new ol.source.Source({}); - var attributions = source.getAttributions(); + var attributions = source.getAttributions2(); expect(attributions).to.be(null); }); + it('accepts a single string', function() { var source = new ol.source.Source({ attributions: 'Humpty' }); - var attributions = source.getAttributions(); + var attributions = source.getAttributions2(); expect(attributions).to.not.be(null); - expect(attributions).to.have.length(1); - expect(attributions[0]).to.be.an(ol.Attribution); - expect(attributions[0].getHTML()).to.be('Humpty'); + expect(typeof attributions).to.be('function'); + expect(attributions()).to.eql(['Humpty']); }); + it('accepts an array of strings', function() { var source = new ol.source.Source({ attributions: ['Humpty', 'Dumpty'] }); - var attributions = source.getAttributions(); + var attributions = source.getAttributions2(); expect(attributions).to.not.be(null); - expect(attributions).to.have.length(2); - expect(attributions[0]).to.be.an(ol.Attribution); - expect(attributions[0].getHTML()).to.be('Humpty'); - expect(attributions[1]).to.be.an(ol.Attribution); - expect(attributions[1].getHTML()).to.be('Dumpty'); + expect(typeof attributions).to.be('function'); + expect(attributions()).to.eql(['Humpty', 'Dumpty']); }); - it('accepts a single ol.Attribution', function() { - var passedAttribution = new ol.Attribution({html: 'Humpty'}); + + it('accepts a function that returns a string', function() { var source = new ol.source.Source({ - attributions: passedAttribution + attributions: function() { + return 'Humpty'; + } }); - var attributions = source.getAttributions(); + var attributions = source.getAttributions2(); expect(attributions).to.not.be(null); - expect(attributions).to.have.length(1); - expect(attributions[0]).to.be.an(ol.Attribution); - expect(attributions[0]).to.be(passedAttribution); + expect(typeof attributions).to.be('function'); + expect(attributions()).to.be('Humpty'); }); - it('accepts an array of ol.Attribution', function() { - var firstAttribution = new ol.Attribution({html: 'Humpty'}); - var secondAttribution = new ol.Attribution({html: 'Dumpty'}); + + it('accepts a function that returns an array of strings', function() { var source = new ol.source.Source({ - attributions: [firstAttribution, secondAttribution] + attributions: function() { + return ['Humpty', 'Dumpty']; + } }); - var attributions = source.getAttributions(); + var attributions = source.getAttributions2(); expect(attributions).to.not.be(null); - expect(attributions).to.have.length(2); - expect(attributions[0]).to.be.an(ol.Attribution); - expect(attributions[0]).to.be(firstAttribution); - expect(attributions[1]).to.be.an(ol.Attribution); - expect(attributions[1]).to.be(secondAttribution); - }); - it('accepts an array with a string and an ol.Attribution', function() { - var attribution = new ol.Attribution({html: 'Dumpty'}); - var source = new ol.source.Source({ - attributions: ['Humpty', attribution] - }); - var attributions = source.getAttributions(); - expect(attributions).to.not.be(null); - expect(attributions).to.have.length(2); - expect(attributions[0]).to.be.an(ol.Attribution); - expect(attributions[0].getHTML()).to.be('Humpty'); - expect(attributions[1]).to.be.an(ol.Attribution); - expect(attributions[1]).to.be(attribution); + expect(typeof attributions).to.be('function'); + expect(attributions()).to.eql(['Humpty', 'Dumpty']); }); }); @@ -96,7 +78,56 @@ describe('ol.source.Source', function() { }); }); - describe('#setAttributions`', function() { + describe('#getAttributions()', function() { + it('maintains backwards compatibility for string option', function() { + var source = new ol.source.Source({ + attributions: 'foo' + }); + var attributions = source.getAttributions(); + expect(attributions.length).to.be(1); + expect(attributions[0]).to.be.an(ol.Attribution); + expect(attributions[0].getHTML()).to.be('foo'); + }); + + it('maintains backwards compatibility for array of strings option', function() { + var source = new ol.source.Source({ + attributions: ['foo', 'bar'] + }); + var attributions = source.getAttributions(); + expect(attributions.length).to.be(2); + expect(attributions[0]).to.be.an(ol.Attribution); + expect(attributions[0].getHTML()).to.be('foo'); + expect(attributions[1]).to.be.an(ol.Attribution); + expect(attributions[1].getHTML()).to.be('bar'); + }); + + it('maintains backwards compatibility for ol.Attribution option', function() { + var source = new ol.source.Source({ + attributions: new ol.Attribution({html: 'foo'}) + }); + var attributions = source.getAttributions(); + expect(attributions.length).to.be(1); + expect(attributions[0]).to.be.an(ol.Attribution); + expect(attributions[0].getHTML()).to.be('foo'); + }); + + it('maintains backwards compatibility for array of strings option', function() { + var source = new ol.source.Source({ + attributions: [ + new ol.Attribution({html: 'foo'}), + new ol.Attribution({html: 'bar'}) + ] + }); + var attributions = source.getAttributions(); + expect(attributions.length).to.be(2); + expect(attributions[0]).to.be.an(ol.Attribution); + expect(attributions[0].getHTML()).to.be('foo'); + expect(attributions[1]).to.be.an(ol.Attribution); + expect(attributions[1].getHTML()).to.be('bar'); + }); + }); + + describe('#setAttributions()', function() { var source = null; beforeEach(function() { @@ -104,64 +135,51 @@ describe('ol.source.Source', function() { attributions: 'before' }); }); + afterEach(function() { source = null; }); it('accepts undefined', function() { source.setAttributions(); - var attributions = source.getAttributions(); + var attributions = source.getAttributions2(); expect(attributions).to.be(null); }); + it('accepts a single string', function() { source.setAttributions('Humpty'); - var attributions = source.getAttributions(); + var attributions = source.getAttributions2(); expect(attributions).to.not.be(null); - expect(attributions).to.have.length(1); - expect(attributions[0]).to.be.an(ol.Attribution); - expect(attributions[0].getHTML()).to.be('Humpty'); + expect(typeof attributions).to.be('function'); + expect(attributions()).to.eql(['Humpty']); }); + it('accepts an array of strings', function() { source.setAttributions(['Humpty', 'Dumpty']); - var attributions = source.getAttributions(); + var attributions = source.getAttributions2(); expect(attributions).to.not.be(null); - expect(attributions).to.have.length(2); - expect(attributions[0]).to.be.an(ol.Attribution); - expect(attributions[0].getHTML()).to.be('Humpty'); - expect(attributions[1]).to.be.an(ol.Attribution); - expect(attributions[1].getHTML()).to.be('Dumpty'); + expect(typeof attributions).to.be('function'); + expect(attributions()).to.eql(['Humpty', 'Dumpty']); }); - it('accepts a single ol.Attribution', function() { - var passedAttribution = new ol.Attribution({html: 'Humpty'}); - source.setAttributions(passedAttribution); - var attributions = source.getAttributions(); + + it('accepts a function that returns a string', function() { + source.setAttributions(function() { + return 'Humpty'; + }); + var attributions = source.getAttributions2(); expect(attributions).to.not.be(null); - expect(attributions).to.have.length(1); - expect(attributions[0]).to.be.an(ol.Attribution); - expect(attributions[0]).to.be(passedAttribution); + expect(typeof attributions).to.be('function'); + expect(attributions()).to.eql('Humpty'); }); - it('accepts an array of ol.Attribution', function() { - var firstAttribution = new ol.Attribution({html: 'Humpty'}); - var secondAttribution = new ol.Attribution({html: 'Dumpty'}); - source.setAttributions([firstAttribution, secondAttribution]); - var attributions = source.getAttributions(); + + it('accepts a function that returns an array of strings', function() { + source.setAttributions(function() { + return ['Humpty', 'Dumpty']; + }); + var attributions = source.getAttributions2(); expect(attributions).to.not.be(null); - expect(attributions).to.have.length(2); - expect(attributions[0]).to.be.an(ol.Attribution); - expect(attributions[0]).to.be(firstAttribution); - expect(attributions[1]).to.be.an(ol.Attribution); - expect(attributions[1]).to.be(secondAttribution); - }); - it('accepts an array with a string and an ol.Attribution', function() { - var attribution = new ol.Attribution({html: 'Dumpty'}); - source.setAttributions(['Humpty', attribution]); - var attributions = source.getAttributions(); - expect(attributions).to.not.be(null); - expect(attributions).to.have.length(2); - expect(attributions[0]).to.be.an(ol.Attribution); - expect(attributions[0].getHTML()).to.be('Humpty'); - expect(attributions[1]).to.be.an(ol.Attribution); - expect(attributions[1]).to.be(attribution); + expect(typeof attributions).to.be('function'); + expect(attributions()).to.eql(['Humpty', 'Dumpty']); }); }); diff --git a/test/spec/ol/source/tileutfgrid.test.js b/test/spec/ol/source/tileutfgrid.test.js index fe15d50165..a9be33f74d 100644 --- a/test/spec/ol/source/tileutfgrid.test.js +++ b/test/spec/ol/source/tileutfgrid.test.js @@ -165,21 +165,15 @@ describe('ol.source.TileUTFGrid', function() { it('sets up correct attribution', function() { var source = getTileUTFGrid(); - expect(source.getAttributions()).to.be(null); + expect(source.getAttributions2()).to.be(null); // call the handleTileJSONResponse method with our // locally available tileJson (from `before`) source.handleTileJSONResponse(tileJson); - var attributions = source.getAttributions(); + var attributions = source.getAttributions2(); expect(attributions).to.not.be(null); - expect(attributions).to.have.length(1); - expect(attributions[0].getHTML()).to.be(tileJson.attribution); - var tileRanges = attributions[0].tileRanges_; - for (var z = tileJson.minzoom; z <= tileJson.maxzoom; z++) { - var key = z.toString(); - expect(key in tileRanges).to.be(true); - } + expect(typeof attributions).to.be('function'); }); it('sets correct state', function() {