diff --git a/src/ol/interaction/Translate.js b/src/ol/interaction/Translate.js index eb2944aa24..b570ed5cca 100644 --- a/src/ol/interaction/Translate.js +++ b/src/ol/interaction/Translate.js @@ -35,6 +35,13 @@ const TranslateEventType = { TRANSLATEEND: 'translateend' }; +/** + * A function that takes an {@link module:ol/Feature} or + * {@link module:ol/render/Feature} and an + * {@link module:ol/layer/Layer} and returns `true` if the feature may be + * translated or `false` otherwise. + * @typedef {function(import("../Feature.js").FeatureLike, import("../layer/Layer.js").default):boolean} FilterFunction + */ /** * @typedef {Object} Options @@ -45,6 +52,10 @@ const TranslateEventType = { * function will be called for each layer in the map and should return * `true` for layers that you want to be translatable. If the option is * absent, all visible layers will be considered translatable. + * @property {FilterFunction} [filter] A function + * that takes an {@link module:ol/Feature} and an + * {@link module:ol/layer/Layer} and returns `true` if the feature may be + * translated or `false` otherwise. * @property {number} [hitTolerance=0] Hit-detection tolerance. Pixels inside the radius around the given position * will be checked for features. */ @@ -136,6 +147,12 @@ class Translate extends PointerInteraction { */ this.layerFilter_ = layerFilter; + /** + * @private + * @type {FilterFunction} + */ + this.filter_ = options.filter ? options.filter : TRUE; + /** * @private * @type {number} @@ -245,9 +262,11 @@ class Translate extends PointerInteraction { */ featuresAtPixel_(pixel, map) { return map.forEachFeatureAtPixel(pixel, - function(feature) { - if (!this.features_ || includes(this.features_.getArray(), feature)) { - return feature; + function(feature, layer) { + if (this.filter_(feature, layer)) { + if (!this.features_ || includes(this.features_.getArray(), feature)) { + return feature; + } } }.bind(this), { layerFilter: this.layerFilter_, diff --git a/test/spec/ol/interaction/translate.test.js b/test/spec/ol/interaction/translate.test.js index c61f9a30c4..d2b9d41825 100644 --- a/test/spec/ol/interaction/translate.test.js +++ b/test/spec/ol/interaction/translate.test.js @@ -216,6 +216,47 @@ describe('ol.interaction.Translate', function() { }); }); + describe('moving features, with filter option', function() { + let translate; + + beforeEach(function() { + translate = new Translate({ + filter: function(feature, layer) { + return feature == features[0]; + } + }); + map.addInteraction(translate); + }); + + it('moves a filter-passing feature', function() { + const events = trackEvents(features[0], translate); + + simulateEvent('pointermove', 10, 20); + simulateEvent('pointerdown', 10, 20); + simulateEvent('pointerdrag', 50, -40); + simulateEvent('pointerup', 50, -40); + const geometry = features[0].getGeometry(); + expect(geometry).to.be.a(Point); + expect(geometry.getCoordinates()).to.eql([50, 40]); + + validateEvents(events, [features[0]]); + }); + + it('does not move a filter-discarded feature', function() { + const events = trackEvents(features[0], translate); + + simulateEvent('pointermove', 20, 30); + simulateEvent('pointerdown', 20, 30); + simulateEvent('pointerdrag', 50, -40); + simulateEvent('pointerup', 50, -40); + const geometry = features[1].getGeometry(); + expect(geometry).to.be.a(Point); + expect(geometry.getCoordinates()).to.eql([20, -30]); + + expect(events).to.be.empty(); + }); + }); + describe('changes css cursor', function() { let element, translate;