From 100b85a7b0f578c2b189e2387f4d172af3da8c13 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Mon, 26 Aug 2013 17:03:14 +0200 Subject: [PATCH] Converting control to an interaction To dispatch events, the interaction base class now inherits from goog.events.EventTarget. --- examples/select-features.html | 48 +--- examples/select-features.js | 10 +- src/objectliterals.jsdoc | 16 +- src/ol/control/selectcontrol.exports | 3 - src/ol/control/selectcontrol.js | 235 ------------------ src/ol/interaction/interaction.js | 4 + src/ol/interaction/selectinteraction.exports | 2 + src/ol/interaction/selectinteraction.js | 180 ++++++++++++++ test/spec/ol/control/selectcontrol.test.js | 137 ---------- .../ol/interaction/selectinteraction.test.js | 84 +++++++ 10 files changed, 281 insertions(+), 438 deletions(-) delete mode 100644 src/ol/control/selectcontrol.exports delete mode 100644 src/ol/control/selectcontrol.js create mode 100644 src/ol/interaction/selectinteraction.exports create mode 100644 src/ol/interaction/selectinteraction.js delete mode 100644 test/spec/ol/control/selectcontrol.test.js create mode 100644 test/spec/ol/interaction/selectinteraction.test.js diff --git a/examples/select-features.html b/examples/select-features.html index 2c6428b330..0a05c475fb 100644 --- a/examples/select-features.html +++ b/examples/select-features.html @@ -9,52 +9,6 @@ Select features example - @@ -84,7 +38,7 @@

Select features example

-

Example of using the Select control. Select features by clicking polygons. Hold the Shift-key to add to the selection. Click the 'S' button to toggle the control's active state.

+

Example of using the Select interaction. Select features by clicking polygons. Hold the Shift-key to add to the selection.

See the select-features.js source to see how this is done.

diff --git a/examples/select-features.js b/examples/select-features.js index 268d21e92d..88f8512e39 100644 --- a/examples/select-features.js +++ b/examples/select-features.js @@ -1,8 +1,8 @@ goog.require('ol.Map'); goog.require('ol.RendererHint'); goog.require('ol.View2D'); -goog.require('ol.control.Select'); -goog.require('ol.control.defaults'); +goog.require('ol.interaction.Select'); +goog.require('ol.interaction.defaults'); goog.require('ol.layer.TileLayer'); goog.require('ol.layer.Vector'); goog.require('ol.parser.ogc.GML_v3'); @@ -46,12 +46,12 @@ var vector = new ol.layer.Vector({ }) }); -var selectControl = new ol.control.Select({ +var selectInteraction = new ol.interaction.Select({ layerFilter: function(layer) { return layer === vector; } }); var map = new ol.Map({ - controls: ol.control.defaults().extend([selectControl]), + interactions: ol.interaction.defaults().extend([selectInteraction]), layers: [raster, vector], renderer: ol.RendererHint.CANVAS, target: 'map', @@ -60,5 +60,3 @@ var map = new ol.Map({ zoom: 4 }) }); - -selectControl.activate(); diff --git a/src/objectliterals.jsdoc b/src/objectliterals.jsdoc index 0267e0b594..239ecf8a23 100644 --- a/src/objectliterals.jsdoc +++ b/src/objectliterals.jsdoc @@ -201,16 +201,6 @@ * Default is '' (empty string). */ -/** - * @typedef {Object} ol.control.SelectOptions - * @property {string|undefined} className CSS class name. Default is 'ol-select'. - * @property {Element|undefined} element Element. - * @property {undefined|function(ol.layer.Layer):boolean} layerFilter Filter - * function to restrict selection to a subset of layers. - * @property {ol.Map|undefined} map Map. - * @property {Element|undefined} target Target. - */ - /** * @typedef {Object} ol.control.ScaleLineOptions * @property {string|undefined} className CSS Class name. Default is 'ol-scale-line'. @@ -305,6 +295,12 @@ * @property {number|undefined} delta Delta. */ +/** + * @typedef {Object} ol.interaction.SelectOptions + * @property {undefined|function(ol.layer.Layer):boolean} layerFilter Filter + * function to restrict selection to a subset of layers. + */ + /** * @typedef {Object} ol.interaction.TouchPanOptions * @property {ol.Kinetic|undefined} kinetic Kinetic. diff --git a/src/ol/control/selectcontrol.exports b/src/ol/control/selectcontrol.exports deleted file mode 100644 index ac9685b9bc..0000000000 --- a/src/ol/control/selectcontrol.exports +++ /dev/null @@ -1,3 +0,0 @@ -@exportClass ol.control.Select ol.control.SelectOptions -@exportProperty ol.control.Select.prototype.activate -@exportProperty ol.control.Select.prototype.deactivate diff --git a/src/ol/control/selectcontrol.js b/src/ol/control/selectcontrol.js deleted file mode 100644 index 17b6e9931e..0000000000 --- a/src/ol/control/selectcontrol.js +++ /dev/null @@ -1,235 +0,0 @@ -goog.provide('ol.control.Select'); - -goog.require('goog.array'); -goog.require('goog.dom'); -goog.require('goog.dom.TagName'); -goog.require('goog.dom.classes'); -goog.require('goog.events'); -goog.require('goog.events.EventType'); -goog.require('ol.MapBrowserEvent.EventType'); -goog.require('ol.control.Control'); -goog.require('ol.css'); -goog.require('ol.interaction.condition'); -goog.require('ol.layer.Vector'); -goog.require('ol.layer.VectorLayerRenderIntent'); -goog.require('ol.source.Vector'); - - -/** - * @typedef {{layer: ol.layer.Layer, - * selected: (Array.|undefined), - * type: goog.events.EventType, - * unselected: (Array.|undefined)}} - */ -ol.control.SelectEventObject; - - - -/** - * @constructor - * @extends {ol.control.Control} - * @param {ol.control.SelectOptions=} opt_options Options. - */ -ol.control.Select = function(opt_options) { - var options = goog.isDef(opt_options) ? opt_options : {}; - - /** - * @type {boolean} - * @private - */ - this.active_ = false; - - /** - * Mapping between original features and cloned features on selection layers. - * @type {Object.<*,Object.>} - * @private - */ - this.featureMap_ = {}; - - /** - * Mapping between original layers and selection layers. - * @type {Object.<*,ol.layer.Vector>} - * @protected - */ - this.selectionLayers = {}; - - /** - * @type {null|function(ol.layer.Layer):boolean} - * @private - */ - this.layerFilter_ = goog.isDef(options.layerFilter) ? - options.layerFilter : null; - - // TODO: css/button refactoring - var className = goog.isDef(options.className) ? options.className : - 'ol-select'; - - var element = goog.dom.createDom(goog.dom.TagName.DIV, { - 'class': className + ' ' + ol.css.CLASS_UNSELECTABLE - }); - var button = goog.dom.createDom(goog.dom.TagName.A, { - 'href': '#Select' - }); - goog.dom.appendChild(element, button); - - goog.events.listen(element, [ - goog.events.EventType.TOUCHEND, - goog.events.EventType.CLICK - ], this.toggleActive_, false, this); - - goog.base(this, { - element: element, - map: options.map, - target: options.target - }); -}; -goog.inherits(ol.control.Select, ol.control.Control); - - -/** - * @param {goog.events.BrowserEvent} browserEvent Browser event. - * @private - */ -ol.control.Select.prototype.toggleActive_ = function(browserEvent) { - // prevent #Select anchor from getting appended to the url - browserEvent.preventDefault(); - if (this.active_) { - this.deactivate(); - } else { - this.activate(); - } -}; - - -/** - * Activate the control. - */ -ol.control.Select.prototype.activate = function() { - if (!this.active_) { - this.active_ = true; - goog.dom.classes.add(this.element, 'active'); - var map = this.getMap(); - for (var i in this.selectionLayers) { - map.addLayer(this.selectionLayers[i]); - } - - // TODO: Implement box selection - this.listenerKeys.push( - goog.events.listen(map, ol.MapBrowserEvent.EventType.CLICK, - this.handleClick, true, this)); - } -}; - - -/** - * Dectivate the control. - */ -ol.control.Select.prototype.deactivate = function() { - if (this.active_) { - if (!goog.array.isEmpty(this.listenerKeys)) { - goog.array.forEach(this.listenerKeys, goog.events.unlistenByKey); - this.listenerKeys.length = 0; - } - var map = this.getMap(); - for (var i in this.selectionLayers) { - map.removeLayer(this.selectionLayers[i]); - } - goog.dom.classes.remove(this.element, 'active'); - this.active_ = false; - } -}; - - -/** - * @param {ol.MapBrowserEvent} evt Event. - */ -ol.control.Select.prototype.handleClick = function(evt) { - var map = this.getMap(); - var layers = map.getLayerGroup().getLayersArray(); - if (!goog.isNull(this.layerFilter_)) { - layers = goog.array.filter(layers, this.layerFilter_); - } - var clear = !ol.interaction.condition.shiftKeyOnly(evt.browserEvent); - - function select(featuresByLayer) { - this.select(featuresByLayer, layers, clear); - } - - map.getFeatures({ - layers: layers, - pixel: evt.getPixel(), - success: goog.bind(select, this) - }); -}; - - -/** - * @param {Array.>} featuresByLayer Features by layer. - * @param {Array.} layers The queried layers. - * @param {boolean} clear Whether the current layer content should be cleared. - */ -ol.control.Select.prototype.select = function(featuresByLayer, layers, clear) { - for (var i = 0, ii = featuresByLayer.length; i < ii; ++i) { - var layer = layers[i]; - var layerId = goog.getUid(layer); - var selectionLayer = this.selectionLayers[layerId]; - if (!goog.isDef(selectionLayer)) { - selectionLayer = new ol.layer.Vector({ - source: new ol.source.Vector({parser: null}), - style: layer.getStyle() - }); - selectionLayer.setTemporary(true); - this.getMap().addLayer(selectionLayer); - this.selectionLayers[layerId] = selectionLayer; - this.featureMap_[layerId] = {}; - } - - var features = featuresByLayer[i]; - var numFeatures = features.length; - var selectedFeatures = []; - var featuresToAdd = []; - var unselectedFeatures = []; - var featuresToRemove = []; - var featureMap = this.featureMap_[layerId]; - for (var j = 0; j < numFeatures; ++j) { - var feature = features[j]; - var featureId = goog.getUid(feature); - var clone = featureMap[featureId]; - if (clone) { - // TODO: make toggle configurable - unselectedFeatures.push(feature); - featuresToRemove.push(clone); - delete featureMap[featureId]; - } - if (clear) { - for (var f in featureMap) { - unselectedFeatures.push(layer.getFeatureWithUid(f)); - featuresToRemove.push(featureMap[f]); - } - featureMap = {}; - this.featureMap_[layerId] = featureMap; - } - if (!clone) { - clone = feature.clone(); - featureMap[featureId] = clone; - clone.renderIntent = ol.layer.VectorLayerRenderIntent.SELECTED; - selectedFeatures.push(feature); - featuresToAdd.push(clone); - } - } - if (goog.isFunction(layer.setRenderIntent)) { - layer.setRenderIntent(ol.layer.VectorLayerRenderIntent.HIDDEN, - selectedFeatures); - layer.setRenderIntent(ol.layer.VectorLayerRenderIntent.DEFAULT, - unselectedFeatures); - } - selectionLayer.removeFeatures(featuresToRemove); - selectionLayer.addFeatures(featuresToAdd); - this.dispatchEvent(/** @type {ol.control.SelectEventObject} */ ({ - layer: layer, - selected: selectedFeatures, - type: goog.events.EventType.CHANGE, - unselected: unselectedFeatures - })); - } -}; diff --git a/src/ol/interaction/interaction.js b/src/ol/interaction/interaction.js index 4fe73093b4..e751fe859e 100644 --- a/src/ol/interaction/interaction.js +++ b/src/ol/interaction/interaction.js @@ -2,6 +2,7 @@ goog.provide('ol.interaction.Interaction'); +goog.require('goog.events.EventTarget'); goog.require('ol.MapBrowserEvent'); goog.require('ol.animation.pan'); goog.require('ol.animation.rotate'); @@ -12,9 +13,12 @@ goog.require('ol.easing'); /** * @constructor + * @extends {goog.events.EventTarget} */ ol.interaction.Interaction = function() { + goog.base(this); }; +goog.inherits(ol.interaction.Interaction, goog.events.EventTarget); /** diff --git a/src/ol/interaction/selectinteraction.exports b/src/ol/interaction/selectinteraction.exports new file mode 100644 index 0000000000..50dd4bd10c --- /dev/null +++ b/src/ol/interaction/selectinteraction.exports @@ -0,0 +1,2 @@ +@exportClass ol.interaction.Select ol.interaction.SelectOptions +@exportProperty ol.interaction.Select.prototype.dispose diff --git a/src/ol/interaction/selectinteraction.js b/src/ol/interaction/selectinteraction.js new file mode 100644 index 0000000000..f86af24bd6 --- /dev/null +++ b/src/ol/interaction/selectinteraction.js @@ -0,0 +1,180 @@ +goog.provide('ol.interaction.Select'); + +goog.require('goog.array'); +goog.require('goog.asserts'); +goog.require('goog.events'); +goog.require('goog.events.EventType'); +goog.require('ol.MapBrowserEvent.EventType'); +goog.require('ol.interaction.Interaction'); +goog.require('ol.interaction.condition'); +goog.require('ol.layer.Vector'); +goog.require('ol.layer.VectorLayerRenderIntent'); +goog.require('ol.source.Vector'); + + +/** + * @typedef {{layer: ol.layer.Layer, + * map: ol.Map, + * selected: (Array.|undefined), + * type: goog.events.EventType, + * unselected: (Array.|undefined)}} + */ +ol.interaction.SelectEventObject; + + + +/** + * @constructor + * @extends {ol.interaction.Interaction} + * @param {ol.interaction.SelectOptions=} opt_options Options. + */ +ol.interaction.Select = function(opt_options) { + var options = goog.isDef(opt_options) ? opt_options : {}; + + /** + * Mapping between original features and cloned features on selection layers. + * @type {Object.<*,Object.<*,ol.Feature>>} + * @private + */ + this.featureMap_ = {}; + + /** + * Mapping between original layers and selection layers, by map. + * @type {Object.<*,{map:ol.Map,layers:Object.<*,ol.layer.Vector>}>} + * @protected + */ + this.selectionLayers = {}; + + /** + * @type {null|function(ol.layer.Layer):boolean} + * @private + */ + this.layerFilter_ = goog.isDef(options.layerFilter) ? + options.layerFilter : null; + + goog.base(this); +}; +goog.inherits(ol.interaction.Select, ol.interaction.Interaction); + + +/** + * @inheritDoc + */ +ol.interaction.Select.prototype.disposeInternal = function() { + for (var m in this.selectionLayers) { + var selectionLayers = this.selectionLayers[m].layers; + var map = this.selectionLayers[m].map; + for (var l in selectionLayers) { + map.removeLayer(selectionLayers[l]); + } + } + goog.base(this, 'disposeInternal'); +}; + + +/** + * @inheritDoc. + */ +ol.interaction.Select.prototype.handleMapBrowserEvent = function(evt) { + if (evt.type === ol.MapBrowserEvent.EventType.CLICK) { + var map = evt.map; + var layers = map.getLayerGroup().getLayersArray(); + if (!goog.isNull(this.layerFilter_)) { + layers = goog.array.filter(layers, this.layerFilter_); + } + var clear = !ol.interaction.condition.shiftKeyOnly(evt.browserEvent); + + var select = function(featuresByLayer) { + this.select(map, featuresByLayer, layers, clear); + }; + + map.getFeatures({ + layers: layers, + pixel: evt.getPixel(), + success: goog.bind(select, this) + }); + } + // TODO: Implement box selection + return true; +}; + + +/** + * @param {ol.Map} map The map where the selction event originated. + * @param {Array.>} featuresByLayer Features by layer. + * @param {Array.} layers The queried layers. + * @param {boolean} clear Whether the current layer content should be cleared. + */ +ol.interaction.Select.prototype.select = + function(map, featuresByLayer, layers, clear) { + var mapId = goog.getUid(map); + for (var i = 0, ii = featuresByLayer.length; i < ii; ++i) { + var layer = layers[i]; + var layerId = goog.getUid(layer); + if (!(mapId in this.selectionLayers)) { + this.selectionLayers[mapId] = {map: map, layers: {}}; + } + var selectionLayer = this.selectionLayers[mapId].layers[layerId]; + if (!goog.isDef(selectionLayer)) { + goog.asserts.assertFunction(layer.getStyle, + 'At least one of the layers has no "getStyle()" function.'); + selectionLayer = new ol.layer.Vector({ + source: new ol.source.Vector({parser: null}), + style: layer.getStyle() + }); + selectionLayer.setTemporary(true); + map.addLayer(selectionLayer); + this.selectionLayers[mapId].layers[layerId] = selectionLayer; + this.featureMap_[layerId] = {}; + } + + var features = featuresByLayer[i]; + var numFeatures = features.length; + var selectedFeatures = []; + var featuresToAdd = []; + var unselectedFeatures = []; + var featuresToRemove = []; + var featureMap = this.featureMap_[layerId]; + var oldFeatureMap = featureMap; + if (clear) { + for (var f in featureMap) { + unselectedFeatures.push(layer.getFeatureWithUid(f)); + featuresToRemove.push(featureMap[f]); + } + featureMap = {}; + this.featureMap_[layerId] = featureMap; + } + for (var j = 0; j < numFeatures; ++j) { + var feature = features[j]; + var featureId = goog.getUid(feature); + var clone = featureMap[featureId]; + if (clone) { + // TODO: make toggle configurable + unselectedFeatures.push(feature); + featuresToRemove.push(clone); + delete featureMap[featureId]; + } else if (!(featureId in oldFeatureMap)) { + clone = feature.clone(); + featureMap[featureId] = clone; + clone.renderIntent = ol.layer.VectorLayerRenderIntent.SELECTED; + selectedFeatures.push(feature); + featuresToAdd.push(clone); + } + } + if (goog.isFunction(layer.setRenderIntent)) { + layer.setRenderIntent(ol.layer.VectorLayerRenderIntent.HIDDEN, + selectedFeatures); + layer.setRenderIntent(ol.layer.VectorLayerRenderIntent.DEFAULT, + unselectedFeatures); + } + selectionLayer.removeFeatures(featuresToRemove); + selectionLayer.addFeatures(featuresToAdd); + this.dispatchEvent(/** @type {ol.interaction.SelectEventObject} */ ({ + layer: layer, + map: map, + selected: selectedFeatures, + type: goog.events.EventType.CHANGE, + unselected: unselectedFeatures + })); + } +}; diff --git a/test/spec/ol/control/selectcontrol.test.js b/test/spec/ol/control/selectcontrol.test.js deleted file mode 100644 index 3bce16ba7d..0000000000 --- a/test/spec/ol/control/selectcontrol.test.js +++ /dev/null @@ -1,137 +0,0 @@ -goog.provide('ol.test.control.Select'); - -describe('ol.control.Select', function() { - var map, target, select, vector, features; - - beforeEach(function() { - target = document.createElement('div'); - target.style.width = '256px'; - target.style.height = '256px'; - document.body.appendChild(target); - map = new ol.Map({ - target: target - }); - features = ol.parser.GeoJSON.read(JSON.stringify({ - 'type': 'FeatureCollection', - 'features': [{ - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [-1, 1] - } - }, { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [1, -1] - } - }] - })); - vector = new ol.layer.Vector({source: new ol.source.Vector({})}); - vector.addFeatures(features); - select = new ol.control.Select({ - layerFilter: function(layer) { return layer === vector; }, - map: map - }); - }); - - afterEach(function() { - goog.dispose(select); - goog.dispose(map); - document.body.removeChild(target); - select = null; - map = null; - target = null; - }); - - describe('DOM creation', function() { - - it('creates the expected DOM elements', function() { - var selectButtons = goog.dom.getElementsByClass('ol-select', target), - selectButton = selectButtons[0], - hasUnselectableCls; - - expect(selectButtons.length).to.be(1); - - hasUnselectableCls = goog.dom.classes.has(selectButton, - 'ol-unselectable'); - expect(hasUnselectableCls).to.be(true); - }); - - it('has an .active class only when activated', function() { - var selectButton = goog.dom.getElementsByClass('ol-select', target)[0]; - select.activate(); - expect(goog.dom.classes.has(selectButton, 'active')).to.be(true); - select.deactivate(); - expect(goog.dom.classes.has(selectButton, 'active')).to.be(false); - }); - - }); - - describe('#activate and #deactivate', function() { - it('adds a temp layer to the map only when active and in use', function() { - expect(map.getLayers().getLength()).to.be(0); - select.activate(); - expect(map.getLayers().getLength()).to.be(0); - select.select([features[0]], [vector]); - expect(map.getLayers().getLength()).to.be(1); - expect(map.getLayers().getAt(0).getTemporary()).to.be(true); - select.deactivate(); - expect(map.getLayers().getLength()).to.be(0); - }); - it('has a private property so it knows if it is active', function() { - expect(select.active_).to.be(false); - select.activate(); - expect(select.active_).to.be(true); - select.deactivate(); - expect(select.active_).to.be(false); - }); - it('toggles active state on click', function() { - var selectButton = goog.dom.getElementsByClass('ol-select', target)[0]; - var event = new goog.events.BrowserEvent({type: 'click'}); - goog.events.fireListeners(selectButton, event.type, false, event); - expect(select.active_).to.be(true); - goog.events.fireListeners(selectButton, event.type, false, event); - expect(select.active_).to.be(false); - }); - }); - - describe('#select', function() { - - it('toggles selection of features', function() { - select.select([features], [vector]); - var layer = select.selectionLayers[goog.getUid(vector)]; - expect(goog.object.getCount(layer.featureCache_.idLookup_)).to.be(2); - select.select([features], [vector]); - expect(goog.object.getCount(layer.featureCache_.idLookup_)).to.be(0); - }); - - it('can append features to an existing selection', function() { - select.select([[features[0]]], [vector]); - select.select([[features[1]]], [vector]); - var layer = select.selectionLayers[goog.getUid(vector)]; - expect(goog.object.getCount(layer.featureCache_.idLookup_)).to.be(2); - }); - - it('can clear a selection before selecting new features', function() { - select.select([[features[0]]], [vector], true); - select.select([[features[1]]], [vector], true); - var layer = select.selectionLayers[goog.getUid(vector)]; - expect(goog.object.getCount(layer.featureCache_.idLookup_)).to.be(1); - }); - - }); - -}); - -goog.require('goog.dispose'); -goog.require('goog.dom'); -goog.require('goog.dom.classes'); -goog.require('goog.events'); -goog.require('goog.events.BrowserEvent'); -goog.require('goog.object'); -goog.require('ol.Map'); -goog.require('ol.control.Select'); -goog.require('ol.layer.Vector'); -goog.require('ol.parser.GeoJSON'); -goog.require('ol.source.Vector'); diff --git a/test/spec/ol/interaction/selectinteraction.test.js b/test/spec/ol/interaction/selectinteraction.test.js new file mode 100644 index 0000000000..12de3a07a6 --- /dev/null +++ b/test/spec/ol/interaction/selectinteraction.test.js @@ -0,0 +1,84 @@ +goog.provide('ol.test.interaction.Select'); + +describe('ol.interaction.Select', function() { + var map, target, select, vector, features; + + beforeEach(function() { + target = document.createElement('div'); + target.style.width = '256px'; + target.style.height = '256px'; + document.body.appendChild(target); + map = new ol.Map({ + target: target + }); + features = ol.parser.GeoJSON.read(JSON.stringify({ + 'type': 'FeatureCollection', + 'features': [{ + 'type': 'Feature', + 'geometry': { + 'type': 'Point', + 'coordinates': [-1, 1] + } + }, { + 'type': 'Feature', + 'geometry': { + 'type': 'Point', + 'coordinates': [1, -1] + } + }] + })); + vector = new ol.layer.Vector({source: new ol.source.Vector({})}); + vector.addFeatures(features); + select = new ol.interaction.Select({ + layerFilter: function(layer) { return layer === vector; } + }); + map.getInteractions().push(select); + }); + + afterEach(function() { + goog.dispose(select); + goog.dispose(map); + document.body.removeChild(target); + select = null; + map = null; + target = null; + }); + + describe('#select', function() { + + it('toggles selection of features', function() { + select.select(map, [features], [vector]); + var layer = select.selectionLayers[goog.getUid(map)] + .layers[goog.getUid(vector)]; + expect(goog.object.getCount(layer.featureCache_.idLookup_)).to.be(2); + select.select(map, [features], [vector]); + expect(goog.object.getCount(layer.featureCache_.idLookup_)).to.be(0); + }); + + it('can append features to an existing selection', function() { + select.select(map, [[features[0]]], [vector]); + select.select(map, [[features[1]]], [vector]); + var layer = select.selectionLayers[goog.getUid(map)] + .layers[goog.getUid(vector)]; + expect(goog.object.getCount(layer.featureCache_.idLookup_)).to.be(2); + }); + + it('can clear a selection before selecting new features', function() { + select.select(map, [[features[0]]], [vector], true); + select.select(map, [[features[1]]], [vector], true); + var layer = select.selectionLayers[goog.getUid(map)] + .layers[goog.getUid(vector)]; + expect(goog.object.getCount(layer.featureCache_.idLookup_)).to.be(1); + }); + + }); + +}); + +goog.require('goog.dispose'); +goog.require('goog.object'); +goog.require('ol.Map'); +goog.require('ol.interaction.Select'); +goog.require('ol.layer.Vector'); +goog.require('ol.parser.GeoJSON'); +goog.require('ol.source.Vector');