From d29e5eaef169187fa94124e8c5a246edacdc64a7 Mon Sep 17 00:00:00 2001 From: jonataswalker Date: Wed, 14 Oct 2015 12:15:53 -0300 Subject: [PATCH] Add method for retrieving ol.Overlay by id --- src/ol/map.js | 80 +++++++++++++++++++++++++- src/ol/overlay.js | 43 ++++++++++++++ test/spec/ol/map.test.js | 108 +++++++++++++++++++++++++++++++++++ test/spec/ol/overlay.test.js | 63 ++++++++++++++++++++ 4 files changed, 291 insertions(+), 3 deletions(-) diff --git a/src/ol/map.js b/src/ol/map.js index c9f92062cb..aec1dd1230 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 @@ -466,7 +473,7 @@ ol.Map = function(options) { * @this {ol.Map} */ function(overlay) { - overlay.setMap(this); + this.addOverlayInternal(overlay); }, this); goog.events.listen(this.overlays_, ol.CollectionEventType.ADD, @@ -474,7 +481,7 @@ ol.Map = function(options) { * @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,7 +489,7 @@ ol.Map = function(options) { * @param {ol.CollectionEvent} event Collection event. */ function(event) { - event.element.setMap(null); + this.removeOverlayInternal(/** @type {ol.Overlay} */ (event.element)); }, false, this); }; @@ -539,6 +546,23 @@ ol.Map.prototype.addOverlay = function(overlay) { }; +/** + * This deals with map's overlay collection changes. + * @param {ol.Overlay} overlay Overlay. + * @protected + */ +ol.Map.prototype.addOverlayInternal = function(overlay) { + var id = overlay.getId(); + if (id !== undefined) { + this.overlayIdIndex_[id.toString()] = overlay; + } + overlay.setMap(this); + goog.events.listen( + overlay, ol.Object.getChangeEventType(overlay.getOverlayIdProperty()), + this.handleOverlayIdChange_, false, 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 +791,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. @@ -1065,6 +1103,28 @@ ol.Map.prototype.handleTileChange_ = function() { }; +/** + * @param {goog.events.Event} event Event. + * @private + */ +ol.Map.prototype.handleOverlayIdChange_ = function(event) { + var overlay = /** @type {ol.Overlay} */ (event.target); + var id = overlay.getId().toString(); + var oldId = event.oldValue; + if (oldId && oldId != id) { + delete this.overlayIdIndex_[oldId]; + } + if (id in this.overlayIdIndex_) { + if (this.overlayIdIndex_[id] !== overlay) { + delete this.overlayIdIndex_[id]; + this.overlayIdIndex_[id] = overlay; + } + } else { + this.overlayIdIndex_[id] = overlay; + } +}; + + /** * @private */ @@ -1245,6 +1305,20 @@ ol.Map.prototype.removeOverlay = function(overlay) { }; +/** + * This deals with map's overlay collection changes. + * @param {ol.Overlay} overlay Overlay. + * @protected + */ +ol.Map.prototype.removeOverlayInternal = function(overlay) { + var id = overlay.getId(); + if (id !== undefined) { + delete this.overlayIdIndex_[id.toString()]; + } + overlay.setMap(null); +}; + + /** * @param {number} time Time. * @private diff --git a/src/ol/overlay.js b/src/ol/overlay.js index 9580560d96..079be33a5d 100644 --- a/src/ol/overlay.js +++ b/src/ol/overlay.js @@ -19,6 +19,7 @@ goog.require('ol.extent'); * @enum {string} */ ol.OverlayProperty = { + ID: 'id', ELEMENT: 'element', MAP: 'map', OFFSET: 'offset', @@ -73,6 +74,12 @@ ol.Overlay = function(options) { goog.base(this); + /** + * @private + * @type {number|string|undefined} + */ + this.id_ = undefined; + /** * @private * @type {boolean} @@ -187,6 +194,17 @@ ol.Overlay.prototype.getElement = function() { }; +/** + * Get the feature identifier. This is an identifier for the overlay and + * is set explicitly by calling {@link ol.Overlay#setId}. + * @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. @@ -211,6 +229,15 @@ ol.Overlay.prototype.getOffset = function() { }; +/** + * Workaround to overcome circular dependency. + * @return {ol.OverlayProperty} + */ +ol.Overlay.prototype.getOverlayIdProperty = function() { + return ol.OverlayProperty.ID; +}; + + /** * Get the current position of this overlay. * @return {ol.Coordinate|undefined} The spatial point that the overlay is @@ -321,6 +348,22 @@ ol.Overlay.prototype.setElement = function(element) { }; +/** + * Set the feature id. The feature id can be used with the + * {@link ol.Map#getOverlayById} method. + * @param {number|string} id The feature id. + * @observable + * @api + */ +ol.Overlay.prototype.setId = function(id) { + goog.asserts.assert(id !== undefined, 'overlay id should be defined'); + if (id != this.id_) { + this.id_ = id; + this.set(ol.OverlayProperty.ID, id); + } +}; + + /** * Set the map to be associated with this overlay. * @param {ol.Map|undefined} map 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..cf2cb4bea4 100644 --- a/test/spec/ol/map.test.js +++ b/test/spec/ol/map.test.js @@ -315,6 +315,113 @@ 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({ + element: overlay_target, + position: [0, 0] + }); + overlay.setId('foo'); + map.addOverlay(overlay); + expect(map.getOverlayById('foo')).to.be(overlay); + }); + + it('returns an overlay by id (set after add)', function() { + overlay = new ol.Overlay({ + element: overlay_target, + position: [0, 0] + }); + map.addOverlay(overlay); + expect(map.getOverlayById('foo')).to.be(null); + overlay.setId('foo'); + expect(map.getOverlayById('foo')).to.be(overlay); + }); + + it('returns null when no overlay is found', function() { + overlay = new ol.Overlay({ + element: overlay_target, + position: [0, 0] + }); + overlay.setId('foo'); + map.addOverlay(overlay); + expect(map.getOverlayById('bar')).to.be(null); + }); + + it('returns null after removing overlay', function() { + overlay = new ol.Overlay({ + element: overlay_target, + position: [0, 0] + }); + overlay.setId('foo'); + map.addOverlay(overlay); + expect(map.getOverlayById('foo')).to.be(overlay); + map.removeOverlay(overlay); + expect(map.getOverlayById('foo')).to.be(null); + }); + + it('returns correct overlay after add/remove/add', function() { + expect(map.getOverlayById('foo')).to.be(null); + var first = new ol.Overlay({ + element: overlay_target, + position: [0, 0] + }); + first.setId('foo'); + map.addOverlay(first); + expect(map.getOverlayById('foo')).to.be(first); + map.removeOverlay(first); + expect(map.getOverlayById('foo')).to.be(null); + var second = new ol.Overlay({ + element: overlay_target, + position: [0, 0] + }); + second.setId('foo'); + map.addOverlay(second); + expect(map.getOverlayById('foo')).to.be(second); + }); + + it('returns correct overlay after add/change', function() { + expect(map.getOverlayById('foo')).to.be(null); + overlay = new ol.Overlay({ + element: overlay_target, + position: [0, 0] + }); + overlay.setId('foo'); + map.addOverlay(overlay); + expect(map.getOverlayById('foo')).to.be(overlay); + overlay.setId('bar'); + expect(map.getOverlayById('foo')).to.be(null); + expect(map.getOverlayById('bar')).to.be(overlay); + }); + }); + }); }); @@ -325,6 +432,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..45f49e5046 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,39 @@ describe('ol.Overlay', function() { }); + describe('#setId()', function() { + var overlay, target; + + beforeEach(function() { + target = document.createElement('div'); + overlay = new ol.Overlay({ + element: target, + position: [0, 0] + }); + map.addOverlay(overlay); + }); + afterEach(function() { + map.removeOverlay(overlay); + }); + + it('sets the overlay identifier', function() { + expect(overlay.getId()).to.be(undefined); + overlay.setId('foo'); + expect(overlay.getId()).to.be('foo'); + }); + + it('accepts a string or number', function() { + overlay.setId('foo'); + expect(overlay.getId()).to.be('foo'); + overlay.setId(2); + expect(overlay.getId()).to.be(2); + }); + + }); + }); +goog.require('goog.dispose'); +goog.require('ol.Map'); goog.require('ol.Overlay'); +goog.require('ol.View');