diff --git a/externs/olx.js b/externs/olx.js index 020241976b..bcfbb01c18 100644 --- a/externs/olx.js +++ b/externs/olx.js @@ -309,7 +309,8 @@ olx.MapOptions.prototype.view; /** * Object literal with config options for the overlay. - * @typedef {{element: (Element|undefined), + * @typedef {{id: (number|string|undefined), + * element: (Element|undefined), * offset: (Array.|undefined), * position: (ol.Coordinate|undefined), * positioning: (ol.OverlayPositioning|string|undefined), @@ -323,6 +324,15 @@ olx.MapOptions.prototype.view; olx.OverlayOptions; +/** + * Set the overlay id. The overlay id can be used with the + * {@link ol.Map#getOverlayById} method. + * @type {number|string|undefined} + * @api + */ +olx.OverlayOptions.prototype.id; + + /** * The overlay element. * @type {Element|undefined} diff --git a/src/ol/map.js b/src/ol/map.js index c9f92062cb..15fda71a25 100644 --- a/src/ol/map.js +++ b/src/ol/map.js @@ -342,6 +342,13 @@ ol.Map = function(options) { */ this.overlays_ = optionsInternal.overlays; + /** + * A lookup of overlays by id. + * @private + * @type {Object.} + */ + this.overlayIdIndex_ = {}; + /** * @type {ol.renderer.Map} * @private @@ -460,21 +467,14 @@ ol.Map = function(options) { event.element.setMap(null); }, false, this); - this.overlays_.forEach( - /** - * @param {ol.Overlay} overlay Overlay. - * @this {ol.Map} - */ - function(overlay) { - overlay.setMap(this); - }, this); + this.overlays_.forEach(this.addOverlayInternal_, this); goog.events.listen(this.overlays_, ol.CollectionEventType.ADD, /** * @param {ol.CollectionEvent} event Collection event. */ function(event) { - event.element.setMap(this); + this.addOverlayInternal_(/** @type {ol.Overlay} */ (event.element)); }, false, this); goog.events.listen(this.overlays_, ol.CollectionEventType.REMOVE, @@ -482,6 +482,10 @@ ol.Map = function(options) { * @param {ol.CollectionEvent} event Collection event. */ function(event) { + var id = event.element.getId(); + if (id !== undefined) { + delete this.overlayIdIndex_[id.toString()]; + } event.element.setMap(null); }, false, this); @@ -539,6 +543,20 @@ ol.Map.prototype.addOverlay = function(overlay) { }; +/** + * This deals with map's overlay collection changes. + * @param {ol.Overlay} overlay Overlay. + * @private + */ +ol.Map.prototype.addOverlayInternal_ = function(overlay) { + var id = overlay.getId(); + if (id !== undefined) { + this.overlayIdIndex_[id.toString()] = overlay; + } + overlay.setMap(this); +}; + + /** * Add functions to be called before rendering. This can be used for attaching * animations before updating the map's view. The {@link ol.animation} @@ -767,6 +785,20 @@ ol.Map.prototype.getOverlays = function() { }; +/** + * Get an overlay by its identifier (the value returned by overlay.getId()). + * Note that the index treats string and numeric identifiers as the same. So + * `map.getOverlayById(2)` will return an overlay with id `'2'` or `2`. + * @param {string|number} id Overlay identifier. + * @return {ol.Overlay} Overlay. + * @api + */ +ol.Map.prototype.getOverlayById = function(id) { + var overlay = this.overlayIdIndex_[id.toString()]; + return overlay !== undefined ? overlay : null; +}; + + /** * Get the map interactions. Modifying this collection changes the interactions * associated with the map. diff --git a/src/ol/overlay.js b/src/ol/overlay.js index 9580560d96..6abd39d9a7 100644 --- a/src/ol/overlay.js +++ b/src/ol/overlay.js @@ -73,6 +73,12 @@ ol.Overlay = function(options) { goog.base(this); + /** + * @private + * @type {number|string|undefined} + */ + this.id_ = options.id; + /** * @private * @type {boolean} @@ -187,6 +193,16 @@ ol.Overlay.prototype.getElement = function() { }; +/** + * Get the overlay identifier which is set on constructor. + * @return {number|string|undefined} Id. + * @api + */ +ol.Overlay.prototype.getId = function() { + return this.id_; +}; + + /** * Get the map associated with this overlay. * @return {ol.Map|undefined} The map that the overlay is part of. diff --git a/test/spec/ol/map.test.js b/test/spec/ol/map.test.js index 85f58ddd36..78ca851136 100644 --- a/test/spec/ol/map.test.js +++ b/test/spec/ol/map.test.js @@ -315,6 +315,69 @@ describe('ol.Map', function() { }); }); + describe('#getOverlayById()', function() { + var target, map, overlay, overlay_target; + + beforeEach(function() { + target = document.createElement('div'); + var style = target.style; + style.position = 'absolute'; + style.left = '-1000px'; + style.top = '-1000px'; + style.width = '360px'; + style.height = '180px'; + document.body.appendChild(target); + map = new ol.Map({ + target: target, + view: new ol.View({ + projection: 'EPSG:4326', + center: [0, 0], + resolution: 1 + }) + }); + overlay_target = document.createElement('div'); + }); + + afterEach(function() { + map.removeOverlay(overlay); + goog.dispose(map); + document.body.removeChild(target); + }); + + it('returns an overlay by id', function() { + overlay = new ol.Overlay({ + id: 'foo', + element: overlay_target, + position: [0, 0] + }); + map.addOverlay(overlay); + expect(map.getOverlayById('foo')).to.be(overlay); + }); + + it('returns null when no overlay is found', function() { + overlay = new ol.Overlay({ + id: 'foo', + element: overlay_target, + position: [0, 0] + }); + map.addOverlay(overlay); + expect(map.getOverlayById('bar')).to.be(null); + }); + + it('returns null after removing overlay', function() { + overlay = new ol.Overlay({ + id: 'foo', + element: overlay_target, + position: [0, 0] + }); + map.addOverlay(overlay); + expect(map.getOverlayById('foo')).to.be(overlay); + map.removeOverlay(overlay); + expect(map.getOverlayById('foo')).to.be(null); + }); + + }); + }); }); @@ -325,6 +388,7 @@ goog.require('goog.events.BrowserEvent'); goog.require('goog.events.EventType'); goog.require('ol.Map'); goog.require('ol.MapEvent'); +goog.require('ol.Overlay'); goog.require('ol.View'); goog.require('ol.interaction'); goog.require('ol.interaction.Interaction'); diff --git a/test/spec/ol/overlay.test.js b/test/spec/ol/overlay.test.js index 3f1f1e3790..468e58d1da 100644 --- a/test/spec/ol/overlay.test.js +++ b/test/spec/ol/overlay.test.js @@ -1,6 +1,36 @@ goog.provide('ol.test.Overlay'); describe('ol.Overlay', function() { + var target, map; + + var width = 360; + var height = 180; + + beforeEach(function() { + target = document.createElement('div'); + + var style = target.style; + style.position = 'absolute'; + style.left = '-1000px'; + style.top = '-1000px'; + style.width = width + 'px'; + style.height = height + 'px'; + document.body.appendChild(target); + + map = new ol.Map({ + target: target, + view: new ol.View({ + projection: 'EPSG:4326', + center: [0, 0], + resolution: 1 + }) + }); + }); + + afterEach(function() { + goog.dispose(map); + document.body.removeChild(target); + }); describe('constructor', function() { @@ -11,6 +41,38 @@ describe('ol.Overlay', function() { }); + describe('#getId()', function() { + var overlay, target; + + beforeEach(function() { + target = document.createElement('div'); + }); + afterEach(function() { + map.removeOverlay(overlay); + }); + + it('returns the overlay identifier', function() { + overlay = new ol.Overlay({ + element: target, + position: [0, 0] + }); + map.addOverlay(overlay); + expect(overlay.getId()).to.be(undefined); + map.removeOverlay(overlay); + overlay = new ol.Overlay({ + id: 'foo', + element: target, + position: [0, 0] + }); + map.addOverlay(overlay); + expect(overlay.getId()).to.be('foo'); + }); + + }); + }); +goog.require('goog.dispose'); +goog.require('ol.Map'); goog.require('ol.Overlay'); +goog.require('ol.View');