diff --git a/src/ol/interaction/Select.js b/src/ol/interaction/Select.js index 497d6f0212..adfa4ae571 100644 --- a/src/ol/interaction/Select.js +++ b/src/ol/interaction/Select.js @@ -9,10 +9,9 @@ import {singleClick, never, shiftKeyOnly, pointerMove} from '../events/condition import {TRUE} from '../functions.js'; import GeometryType from '../geom/GeometryType.js'; import Interaction from './Interaction.js'; -import VectorLayer from '../layer/Vector.js'; import {clear} from '../obj.js'; -import VectorSource from '../source/Vector.js'; import {createEditingStyle} from '../style/Style.js'; +import Collection from '../Collection.js'; /** @@ -205,23 +204,25 @@ class Select extends Interaction { */ this.hitTolerance_ = options.hitTolerance ? options.hitTolerance : 0; - const featureOverlay = new VectorLayer({ - source: new VectorSource({ - useSpatialIndex: false, - features: options.features, - wrapX: options.wrapX - }), - style: options.style ? options.style : - getDefaultStyleFunction(), - updateWhileAnimating: true, - updateWhileInteracting: true - }); + /** + * @private + * @type {import("../style/Style.js").default|Array.|import("../style/Style.js").StyleFunction|null} + */ + this.style_ = options.style ? options.style : getDefaultStyleFunction(); + + /** + * An association between selected feature (key) + * and original style (value) + * @private + * @type {Object.|import("../style/Style.js").StyleFunction>} + */ + this.featureStyleAssociation_ = {}; /** * @private - * @type {VectorLayer} + * @type {import("../Collection.js").default} */ - this.featureOverlay_ = featureOverlay; + this.features_ = options.features || new Collection(); /** @type {function(import("../layer/Layer.js").default): boolean} */ let layerFilter; @@ -272,7 +273,7 @@ class Select extends Interaction { * @api */ getFeatures() { - return this.featureOverlay_.getSource().getFeaturesCollection(); + return this.features_; } /** @@ -290,24 +291,15 @@ class Select extends Interaction { * programmatic method like pushing features to * {@link module:ol/interaction/Select~Select#getFeatures collection}. * @param {import("../Feature.js").FeatureLike} feature Feature - * @return {VectorLayer} Layer. + * @return {import('../layer/Vector.js').default} Layer. * @api */ getLayer(feature) { return ( - /** @type {VectorLayer} */ (this.featureLayerAssociation_[getUid(feature)]) + /** @type {import('../layer/Vector.js').default} */ (this.featureLayerAssociation_[getUid(feature)]) ); } - /** - * Get the overlay layer that this interaction renders selected features to. - * @return {VectorLayer} Overlay layer. - * @api - */ - getOverlay() { - return this.featureOverlay_; - } - /** * Hit-detection tolerance. Pixels inside the radius around the given position * will be checked for features. @@ -327,14 +319,12 @@ class Select extends Interaction { */ setMap(map) { const currentMap = this.getMap(); - const selectedFeatures = this.getFeatures(); - if (currentMap) { - selectedFeatures.forEach(currentMap.unskipFeature.bind(currentMap)); + if (currentMap && this.style_) { + this.features_.forEach(this.removeSelectedStyle_.bind(this)); } super.setMap(map); - this.featureOverlay_.setMap(map); - if (map) { - selectedFeatures.forEach(map.skipFeature.bind(map)); + if (map && this.style_) { + this.features_.forEach(this.giveSelectedStyle_.bind(this)); } } @@ -343,9 +333,9 @@ class Select extends Interaction { * @private */ addFeature_(evt) { - const map = this.getMap(); - if (map) { - map.skipFeature(/** @type {import("../Feature.js").default} */ (evt.element)); + const feature = evt.element; + if (this.style_) { + this.giveSelectedStyle_(feature); } } @@ -354,12 +344,32 @@ class Select extends Interaction { * @private */ removeFeature_(evt) { - const map = this.getMap(); - if (map) { - map.unskipFeature(/** @type {import("../Feature.js").default} */ (evt.element)); + const feature = evt.element; + if (this.style_) { + this.removeSelectedStyle_(feature); } } + /** + * @param {import("../Feature.js").default} feature Feature + * @private + */ + giveSelectedStyle_(feature) { + const key = getUid(feature); + this.featureStyleAssociation_[key] = feature.getStyle(); + feature.setStyle(this.style_); + } + + /** + * @param {import("../Feature.js").default} feature Feature + * @private + */ + removeSelectedStyle_(feature) { + const key = getUid(feature); + feature.setStyle(this.featureStyleAssociation_[key]); + delete this.featureStyleAssociation_[key]; + } + /** * @param {import("../Feature.js").FeatureLike} feature Feature. * @private @@ -471,7 +481,7 @@ function getDefaultStyleFunction() { extend(styles[GeometryType.POLYGON], styles[GeometryType.LINE_STRING]); extend(styles[GeometryType.GEOMETRY_COLLECTION], styles[GeometryType.LINE_STRING]); - return function(feature, resolution) { + return function(feature) { if (!feature.getGeometry()) { return null; } diff --git a/test/spec/ol/interaction/select.test.js b/test/spec/ol/interaction/select.test.js index 9dc9e562e1..cdfb8bebb9 100644 --- a/test/spec/ol/interaction/select.test.js +++ b/test/spec/ol/interaction/select.test.js @@ -259,10 +259,10 @@ describe('ol.interaction.Select', function() { simulateEvent('singleclick', 10, -20, true); - expect(listenerSpy.callCount).to.be(2); + expect(listenerSpy.callCount).to.be(1); features = select.getFeatures(); - expect(features.getLength()).to.equal(0); + expect(features.getLength()).to.equal(4); }); }); @@ -377,8 +377,6 @@ describe('ol.interaction.Select', function() { map.addInteraction(interaction); - expect(interaction.featureOverlay_).not.to.be(null); - simulateEvent('singleclick', 10, -20); }); @@ -406,46 +404,4 @@ describe('ol.interaction.Select', function() { }); }); - - describe('#setMap()', function() { - let interaction; - - beforeEach(function() { - interaction = new Select(); - expect(interaction.getActive()).to.be(true); - }); - - describe('#setMap(null)', function() { - beforeEach(function() { - map.addInteraction(interaction); - }); - afterEach(function() { - map.removeInteraction(interaction); - }); - describe('#setMap(null) when interaction is active', function() { - it('unsets the map from the feature overlay', function() { - const spy = sinon.spy(interaction.featureOverlay_, 'setMap'); - interaction.setMap(null); - expect(spy.getCall(0).args[0]).to.be(null); - }); - }); - }); - - describe('#setMap(map)', function() { - describe('#setMap(map) when interaction is active', function() { - it('sets the map into the feature overlay', function() { - const spy = sinon.spy(interaction.featureOverlay_, 'setMap'); - interaction.setMap(map); - expect(spy.getCall(0).args[0]).to.be(map); - }); - }); - }); - }); - - describe('#getOverlay', function() { - it('returns the feature overlay layer', function() { - const select = new Select(); - expect (select.getOverlay()).to.eql(select.featureOverlay_); - }); - }); });