diff --git a/css/ol.css b/css/ol.css index f22a2b9c1b..448735abf7 100644 --- a/css/ol.css +++ b/css/ol.css @@ -24,6 +24,19 @@ position: absolute; border: 2px solid red; } +.ol-logo { + bottom: 0; + left: 0; + padding: 2px; + position: absolute; +} +.ol-logo ul { + margin: 0; +} +.ol-logo ul li { + display: inline; + list-style: none; +} .ol-scale-line { background: rgba(0,60,136,0.3); border-radius: 4px; diff --git a/src/objectliterals.exports b/src/objectliterals.exports index 593cae744f..ce9a64c542 100644 --- a/src/objectliterals.exports +++ b/src/objectliterals.exports @@ -49,9 +49,15 @@ @exportObjectLiteral ol.control.DefaultsOptions @exportObjectLiteralProperty ol.control.DefaultsOptions.attribution boolean|undefined @exportObjectLiteralProperty ol.control.DefaultsOptions.attributionOptions ol.control.AttributionOptions|undefined +@exportObjectLiteralProperty ol.control.DefaultsOptions.logo boolean|undefined +@exportObjectLiteralProperty ol.control.DefaultsOptions.logoOptions ol.control.LogoOptions|undefined @exportObjectLiteralProperty ol.control.DefaultsOptions.zoom boolean|undefined @exportObjectLiteralProperty ol.control.DefaultsOptions.zoomOptions ol.control.ZoomOptions|undefined +@exportObjectLiteral ol.control.LogoOptions +@exportObjectLiteralProperty ol.control.LogoOptions.map ol.Map|undefined +@exportObjectLiteralProperty ol.control.LogoOptions.target Element|undefined + @exportObjectLiteral ol.control.ScaleLineOptions @exportObjectLiteralProperty ol.control.ScaleLineOptions.map ol.Map|undefined @exportObjectLiteralProperty ol.control.ScaleLineOptions.minWidth number|undefined @@ -145,6 +151,7 @@ @exportObjectLiteral ol.source.SourceOptions @exportObjectLiteralProperty ol.source.SourceOptions.attributions Array.|undefined @exportObjectLiteralProperty ol.source.SourceOptions.extent ol.Extent|undefined +@exportObjectLiteralProperty ol.source.SourceOptions.logo string|undefined @exportObjectLiteralProperty ol.source.SourceOptions.projection ol.ProjectionLike @exportObjectLiteral ol.source.StamenOptions diff --git a/src/ol/control/defaults.js b/src/ol/control/defaults.js index 90980c182b..95fa4d9f12 100644 --- a/src/ol/control/defaults.js +++ b/src/ol/control/defaults.js @@ -2,6 +2,7 @@ goog.provide('ol.control.defaults'); goog.require('goog.array'); goog.require('ol.control.Attribution'); +goog.require('ol.control.Logo'); goog.require('ol.control.Zoom'); @@ -25,6 +26,14 @@ ol.control.defaults = function(opt_options, opt_controls) { controls.push(new ol.control.Attribution(attributionControlOptions)); } + var logoControl = goog.isDef(options.logo) ? + options.logo : true; + if (logoControl) { + var logoControlOptions = goog.isDef(options.logoOptions) ? + options.logoOptions : undefined; + controls.push(new ol.control.Logo(logoControlOptions)); + } + var zoomControl = goog.isDef(options.zoom) ? options.zoom : true; if (zoomControl) { diff --git a/src/ol/control/logocontrol.js b/src/ol/control/logocontrol.js new file mode 100644 index 0000000000..69897eaef1 --- /dev/null +++ b/src/ol/control/logocontrol.js @@ -0,0 +1,129 @@ +goog.provide('ol.control.Logo'); + +goog.require('goog.dom'); +goog.require('goog.dom.TagName'); +goog.require('goog.events'); +goog.require('goog.object'); +goog.require('goog.style'); +goog.require('ol.FrameState'); +goog.require('ol.MapEvent'); +goog.require('ol.MapEventType'); +goog.require('ol.control.Control'); + + + +/** + * @constructor + * @extends {ol.control.Control} + * @param {ol.control.LogoOptions=} opt_options Options. + */ +ol.control.Logo = function(opt_options) { + + var options = goog.isDef(opt_options) ? opt_options : {}; + + /** + * @private + * @type {Element} + */ + this.ulElement_ = goog.dom.createElement(goog.dom.TagName.UL); + + var element = goog.dom.createDom(goog.dom.TagName.DIV, { + 'class': 'ol-logo ' + ol.CSS_CLASS_UNSELECTABLE + }, this.ulElement_); + + goog.base(this, { + element: element, + map: options.map, + target: options.target + }); + + /** + * @private + * @type {boolean} + */ + this.renderedVisible_ = true; + + /** + * @private + * @type {Object.} + */ + this.logoElements_ = {}; + + /** + * @private + * @type {?number} + */ + this.postrenderListenKey_ = null; + +}; +goog.inherits(ol.control.Logo, ol.control.Control); + + +/** + * @param {ol.MapEvent} mapEvent Map event. + */ +ol.control.Logo.prototype.handleMapPostrender = function(mapEvent) { + this.updateElement_(mapEvent.frameState); +}; + + +/** + * @inheritDoc + */ +ol.control.Logo.prototype.setMap = function(map) { + if (!goog.isNull(this.postrenderListenKey_)) { + goog.events.unlistenByKey(this.postrenderListenKey_); + this.postrenderListenKey_ = null; + } + goog.base(this, 'setMap', map); + if (!goog.isNull(map)) { + this.postrenderListenKey_ = goog.events.listen( + map, ol.MapEventType.POSTRENDER, this.handleMapPostrender, false, this); + } +}; + + +/** + * @param {?ol.FrameState} frameState Frame state. + * @private + */ +ol.control.Logo.prototype.updateElement_ = function(frameState) { + + if (goog.isNull(frameState)) { + if (this.renderedVisible_) { + goog.style.showElement(this.element, false); + this.renderedVisible_ = false; + } + return; + } + + var logo; + var logos = frameState.logos; + var logoElements = this.logoElements_; + + for (logo in logoElements) { + if (!(logo in logos)) { + goog.dom.removeNode(logoElements[logo]); + delete logoElements[logo]; + } + } + + var image, logoElement; + for (logo in logos) { + if (!(logo in logoElements)) { + image = new Image(); + image.src = logo; + logoElement = goog.dom.createElement(goog.dom.TagName.LI); + logoElement.appendChild(image); + goog.dom.appendChild(this.ulElement_, logoElement); + logoElements[logo] = logoElement; + } + } + + var renderVisible = !goog.object.isEmpty(logos); + if (this.renderedVisible_ != renderVisible) { + goog.style.showElement(this.element, renderVisible); + this.renderedVisible_ = renderVisible; + } + +}; diff --git a/src/ol/framestate.js b/src/ol/framestate.js index cffecf36b4..5ab8fdb9a7 100644 --- a/src/ol/framestate.js +++ b/src/ol/framestate.js @@ -26,6 +26,7 @@ goog.require('ol.layer.LayerState'); * focus: ol.Coordinate, * layersArray: Array., * layerStates: Object., + * logos: Object., * pixelToCoordinateMatrix: goog.vec.Mat4.Number, * postRenderFunctions: Array., * size: ol.Size, diff --git a/src/ol/map.js b/src/ol/map.js index 56b0f12355..b2f1a29b9f 100644 --- a/src/ol/map.js +++ b/src/ol/map.js @@ -754,6 +754,7 @@ ol.Map.prototype.renderFrame_ = function(time) { focus: goog.isNull(this.focus_) ? view2DState.center : this.focus_, layersArray: layersArray, layerStates: layerStates, + logos: {}, pixelToCoordinateMatrix: this.pixelToCoordinateMatrix_, postRenderFunctions: [], size: size, diff --git a/src/ol/renderer/canvas/canvasimagelayerrenderer.js b/src/ol/renderer/canvas/canvasimagelayerrenderer.js index b1ff6ba2ec..deff997d11 100644 --- a/src/ol/renderer/canvas/canvasimagelayerrenderer.js +++ b/src/ol/renderer/canvas/canvasimagelayerrenderer.js @@ -115,5 +115,6 @@ ol.renderer.canvas.ImageLayer.prototype.renderFrame = 0); this.updateAttributions(frameState.attributions, image.getAttributions()); + this.updateLogos(frameState, imageSource); } }; diff --git a/src/ol/renderer/canvas/canvastilelayerrenderer.js b/src/ol/renderer/canvas/canvastilelayerrenderer.js index 31e1714ee9..e700cba2ee 100644 --- a/src/ol/renderer/canvas/canvastilelayerrenderer.js +++ b/src/ol/renderer/canvas/canvastilelayerrenderer.js @@ -286,6 +286,7 @@ ol.renderer.canvas.TileLayer.prototype.renderFrame = this.manageTilePyramid(frameState, tileSource, tileGrid, projection, extent, z, tileLayer.getPreload()); this.scheduleExpireCache(frameState, tileSource); + this.updateLogos(frameState, tileSource); var transform = this.transform_; goog.vec.Mat4.makeIdentity(transform); diff --git a/src/ol/renderer/dom/domimagelayerrenderer.js b/src/ol/renderer/dom/domimagelayerrenderer.js index 265edd5f03..bc07ae2776 100644 --- a/src/ol/renderer/dom/domimagelayerrenderer.js +++ b/src/ol/renderer/dom/domimagelayerrenderer.js @@ -109,6 +109,7 @@ ol.renderer.dom.ImageLayer.prototype.renderFrame = this.setTransform(transform); this.updateAttributions(frameState.attributions, image.getAttributions()); + this.updateLogos(frameState, imageSource); } }; diff --git a/src/ol/renderer/dom/domtilelayerrenderer.js b/src/ol/renderer/dom/domtilelayerrenderer.js index a953658797..e2cff9f1f2 100644 --- a/src/ol/renderer/dom/domtilelayerrenderer.js +++ b/src/ol/renderer/dom/domtilelayerrenderer.js @@ -221,6 +221,7 @@ ol.renderer.dom.TileLayer.prototype.renderFrame = this.manageTilePyramid(frameState, tileSource, tileGrid, projection, extent, z, tileLayer.getPreload()); this.scheduleExpireCache(frameState, tileSource); + this.updateLogos(frameState, tileSource); }; diff --git a/src/ol/renderer/layerrenderer.js b/src/ol/renderer/layerrenderer.js index 7a6f4e416f..72b3db2bbd 100644 --- a/src/ol/renderer/layerrenderer.js +++ b/src/ol/renderer/layerrenderer.js @@ -14,6 +14,7 @@ goog.require('ol.TileState'); goog.require('ol.layer.Layer'); goog.require('ol.layer.LayerProperty'); goog.require('ol.layer.LayerState'); +goog.require('ol.source.Source'); goog.require('ol.source.TileSource'); @@ -220,6 +221,19 @@ ol.renderer.Layer.prototype.updateAttributions = }; +/** + * @protected + * @param {ol.FrameState} frameState Frame state. + * @param {ol.source.Source} source Source. + */ +ol.renderer.Layer.prototype.updateLogos = function(frameState, source) { + var logo = source.getLogo(); + if (goog.isDef(logo)) { + frameState.logos[logo] = true; + } +}; + + /** * @protected * @param {Object.>} usedTiles Used tiles. diff --git a/src/ol/renderer/webgl/webglimagelayerrenderer.js b/src/ol/renderer/webgl/webglimagelayerrenderer.js index 8a9e8b68e1..e4658636ca 100644 --- a/src/ol/renderer/webgl/webglimagelayerrenderer.js +++ b/src/ol/renderer/webgl/webglimagelayerrenderer.js @@ -137,6 +137,7 @@ ol.renderer.webgl.ImageLayer.prototype.renderFrame = this.texture = texture; this.updateAttributions(frameState.attributions, image.getAttributions()); + this.updateLogos(frameState, imageSource); } }; diff --git a/src/ol/renderer/webgl/webgltilelayerrenderer.js b/src/ol/renderer/webgl/webgltilelayerrenderer.js index da9a405349..2b3b082b3f 100644 --- a/src/ol/renderer/webgl/webgltilelayerrenderer.js +++ b/src/ol/renderer/webgl/webgltilelayerrenderer.js @@ -282,6 +282,7 @@ ol.renderer.webgl.TileLayer.prototype.renderFrame = } }, this); this.scheduleExpireCache(frameState, tileSource); + this.updateLogos(frameState, tileSource); var texCoordMatrix = this.texCoordMatrix; goog.vec.Mat4.makeIdentity(texCoordMatrix); diff --git a/src/ol/source/bingmapssource.js b/src/ol/source/bingmapssource.js index 3e28a73bea..a141f9fd4f 100644 --- a/src/ol/source/bingmapssource.js +++ b/src/ol/source/bingmapssource.js @@ -147,6 +147,8 @@ ol.source.BingMaps.prototype.handleImageryMetadataResponse = }); this.setAttributions(attributions); + this.setLogo(brandLogoUri); + this.ready_ = true; this.dispatchLoadEvent(); diff --git a/src/ol/source/imagesource.js b/src/ol/source/imagesource.js index 8935dc541d..ca0657344f 100644 --- a/src/ol/source/imagesource.js +++ b/src/ol/source/imagesource.js @@ -16,6 +16,7 @@ goog.require('ol.source.Source'); * @typedef {{attributions: (Array.|undefined), * crossOrigin: (null|string|undefined), * extent: (null|ol.Extent|undefined), + * logo: (string|undefined), * projection: ol.ProjectionLike, * resolutions: (Array.|undefined), * imageUrlFunction: (ol.ImageUrlFunctionType| @@ -36,6 +37,7 @@ ol.source.ImageSource = function(options) { goog.base(this, { attributions: options.attributions, extent: options.extent, + logo: options.logo, projection: options.projection }); diff --git a/src/ol/source/imagetilesource.js b/src/ol/source/imagetilesource.js index 9df28a9328..ed8a65b688 100644 --- a/src/ol/source/imagetilesource.js +++ b/src/ol/source/imagetilesource.js @@ -19,6 +19,7 @@ goog.require('ol.tilegrid.TileGrid'); * @typedef {{attributions: (Array.|undefined), * crossOrigin: (null|string|undefined), * extent: (ol.Extent|undefined), + * logo: (string|undefined), * opaque: (boolean|undefined), * projection: ol.ProjectionLike, * tileGrid: (ol.tilegrid.TileGrid|undefined), @@ -38,6 +39,7 @@ ol.source.ImageTileSource = function(options) { goog.base(this, { attributions: options.attributions, extent: options.extent, + logo: options.logo, opaque: options.opaque, projection: options.projection, tileGrid: options.tileGrid diff --git a/src/ol/source/mapquestsource.js b/src/ol/source/mapquestsource.js index 43ef01ce50..10827beff2 100644 --- a/src/ol/source/mapquestsource.js +++ b/src/ol/source/mapquestsource.js @@ -15,8 +15,7 @@ ol.source.MapQuestOSM = function() { var attributions = [ new ol.Attribution( 'Tiles Courtesy of ' + - 'MapQuest ' + - ''), + 'MapQuest'), new ol.Attribution( 'Data © ' + 'OpenStreetMap ' + @@ -27,6 +26,7 @@ ol.source.MapQuestOSM = function() { goog.base(this, { attributions: attributions, crossOrigin: 'anonymous', + logo: 'http://developer.mapquest.com/content/osm/mq_logo.png', opaque: true, maxZoom: 28, url: 'http://otile{1-4}.mqcdn.com/tiles/1.0.0/osm/{z}/{x}/{y}.jpg' @@ -46,8 +46,7 @@ ol.source.MapQuestOpenAerial = function() { var attributions = [ new ol.Attribution( 'Tiles Courtesy of ' + - 'MapQuest ' + - ''), + 'MapQuest'), new ol.Attribution( 'Portions Courtesy NASA/JPL-Caltech and ' + 'U.S. Depart. of Agriculture, Farm Service Agency') @@ -56,6 +55,7 @@ ol.source.MapQuestOpenAerial = function() { goog.base(this, { attributions: attributions, crossOrigin: 'anonymous', + logo: 'http://developer.mapquest.com/content/osm/mq_logo.png', maxZoom: 18, opaque: true, url: 'http://oatile{1-4}.mqcdn.com/tiles/1.0.0/sat/{z}/{x}/{y}.jpg' diff --git a/src/ol/source/source.js b/src/ol/source/source.js index 84dcbee6bd..8d4733a742 100644 --- a/src/ol/source/source.js +++ b/src/ol/source/source.js @@ -39,6 +39,12 @@ ol.source.Source = function(sourceOptions) { this.attributions_ = goog.isDef(sourceOptions.attributions) ? sourceOptions.attributions : null; + /** + * @private + * @type {string|undefined} + */ + this.logo_ = sourceOptions.logo; + }; goog.inherits(ol.source.Source, goog.events.EventTarget); @@ -67,6 +73,14 @@ ol.source.Source.prototype.getExtent = function() { }; +/** + * @return {string|undefined} Logo. + */ +ol.source.Source.prototype.getLogo = function() { + return this.logo_; +}; + + /** * @return {ol.Projection} Projection. */ @@ -103,6 +117,14 @@ ol.source.Source.prototype.setExtent = function(extent) { }; +/** + * @param {string|undefined} logo Logo. + */ +ol.source.Source.prototype.setLogo = function(logo) { + this.logo_ = logo; +}; + + /** * @param {ol.Projection} projection Projetion. */ diff --git a/src/ol/source/tilesource.js b/src/ol/source/tilesource.js index 6ef41ebbdb..6680e3bb43 100644 --- a/src/ol/source/tilesource.js +++ b/src/ol/source/tilesource.js @@ -14,6 +14,7 @@ goog.require('ol.tilegrid.TileGrid'); /** * @typedef {{attributions: (Array.|undefined), * extent: (ol.Extent|undefined), + * logo: (string|undefined), * opaque: (boolean|undefined), * projection: ol.ProjectionLike, * tileGrid: (ol.tilegrid.TileGrid|undefined)}} @@ -32,6 +33,7 @@ ol.source.TileSource = function(tileSourceOptions) { goog.base(this, { attributions: tileSourceOptions.attributions, extent: tileSourceOptions.extent, + logo: tileSourceOptions.logo, projection: tileSourceOptions.projection }); diff --git a/src/ol/source/xyzsource.js b/src/ol/source/xyzsource.js index 612e6eadc4..186aaa5344 100644 --- a/src/ol/source/xyzsource.js +++ b/src/ol/source/xyzsource.js @@ -19,6 +19,7 @@ goog.require('ol.tilegrid.XYZ'); * @typedef {{attributions: (Array.|undefined), * crossOrigin: (string|undefined), * extent: (ol.Extent|undefined), + * logo: (string|undefined), * maxZoom: number, * projection: (ol.Projection|undefined), * tileUrlFunction: (ol.TileUrlFunctionType|undefined), @@ -105,6 +106,7 @@ ol.source.XYZ = function(xyzOptions) { attributions: xyzOptions.attributions, crossOrigin: xyzOptions.crossOrigin, extent: xyzOptions.extent, + logo: xyzOptions.logo, projection: projection, tileGrid: tileGrid, tileUrlFunction: tileUrlFunction