diff --git a/changelog/upgrade-notes.md b/changelog/upgrade-notes.md index cd618ad0b1..7a37f70929 100644 --- a/changelog/upgrade-notes.md +++ b/changelog/upgrade-notes.md @@ -279,6 +279,10 @@ The `getGetFeatureInfoUrl` of `ol/source/ImageWMS` and `ol/source/TileWMS` is no `getFeaturesAtPixel` now returns an empty array instead of null if no features were found. +##### Hit detection with unfilled styles + +Hit detection over styled Circle geometry and Circle and RegularShape styles is now consistent with that for styled Polygon geometry. There is no hit detection over the interior of unfilled shapes. To get the previous behavior, specify a Fill style with transparent color. + #### Other changes ##### Allow declutter in image render mode diff --git a/src/ol/style/RegularShape.js b/src/ol/style/RegularShape.js index bc894807be..bdf6b89d82 100644 --- a/src/ol/style/RegularShape.js +++ b/src/ol/style/RegularShape.js @@ -2,6 +2,7 @@ * @module ol/style/RegularShape */ +import {asArray} from '../color.js'; import {asColorLike} from '../colorlike.js'; import {createCanvasContext2D} from '../dom.js'; import ImageState from '../ImageState.js'; @@ -429,17 +430,31 @@ class RegularShape extends ImageStyle { */ createHitDetectionCanvas_(renderOptions) { this.hitDetectionImageSize_ = [renderOptions.size, renderOptions.size]; + this.hitDetectionCanvas_ = this.canvas_; if (this.fill_) { - this.hitDetectionCanvas_ = this.canvas_; - return; + let color = this.fill_.getColor(); + + // determine if fill is transparent (or pattern or gradient) + let opacity = 0; + if (typeof color === 'string') { + color = asArray(color); + } + if (color === null) { + opacity = 1; + } else if (Array.isArray(color)) { + opacity = color.length === 4 ? color[3] : 1; + } + if (opacity === 0) { + + // if a transparent fill style is set, create an extra hit-detection image + // with a default fill style + const context = createCanvasContext2D(renderOptions.size, renderOptions.size); + this.hitDetectionCanvas_ = context.canvas; + + this.drawHitDetectionCanvas_(renderOptions, context, 0, 0); + } } - // if no fill style is set, create an extra hit-detection image with a - // default fill style - const context = createCanvasContext2D(renderOptions.size, renderOptions.size); - this.hitDetectionCanvas_ = context.canvas; - - this.drawHitDetectionCanvas_(renderOptions, context, 0, 0); } /** diff --git a/test/spec/ol/renderer/canvas/vectorlayer.test.js b/test/spec/ol/renderer/canvas/vectorlayer.test.js index cb6cad9e99..368a4c5ade 100644 --- a/test/spec/ol/renderer/canvas/vectorlayer.test.js +++ b/test/spec/ol/renderer/canvas/vectorlayer.test.js @@ -393,23 +393,21 @@ describe('ol.renderer.canvas.VectorLayer', function() { geometry: new Circle([7.5, 7.5], 1.5), fillType: 'transparent' }), - // CircleStyle transparent and no fill hit detection - // is currently the opposite of ol/Style new Feature({ geometry: new Point([1.5, 1.5]), - fillType: 'transparent' + fillType: 'none' }), new Feature({ geometry: new Point([2.5, 2.5]), - fillType: 'transparent' + fillType: 'none' }), new Feature({ geometry: new Point([6.5, 1.5]), - fillType: 'none' + fillType: 'transparent' }), new Feature({ geometry: new Point([7.5, 2.5]), - fillType: 'none' + fillType: 'transparent' }) ] }); diff --git a/test/spec/ol/style/circle.test.js b/test/spec/ol/style/circle.test.js index d7df8fd39d..08168bb4a6 100644 --- a/test/spec/ol/style/circle.test.js +++ b/test/spec/ol/style/circle.test.js @@ -14,13 +14,31 @@ describe('ol.style.Circle', function() { expect(style.getImageSize()).to.eql([21, 21]); expect(style.getOrigin()).to.eql([0, 0]); expect(style.getAnchor()).to.eql([10.5, 10.5]); - // hit-detection image is created, because no fill style is set + // no hit-detection image is created, because no fill style is set + expect(style.getImage()).to.be(style.getHitDetectionImage()); + expect(style.getHitDetectionImage()).to.be.an(HTMLCanvasElement); + expect(style.getHitDetectionImageSize()).to.eql([21, 21]); + }); + + it('creates a canvas (transparent fill-style)', function() { + const style = new CircleStyle({ + radius: 10, + fill: new Fill({ + color: 'transparent' + }) + }); + expect(style.getImage()).to.be.an(HTMLCanvasElement); + expect(style.getSize()).to.eql([21, 21]); + expect(style.getImageSize()).to.eql([21, 21]); + expect(style.getOrigin()).to.eql([0, 0]); + expect(style.getAnchor()).to.eql([10.5, 10.5]); + // hit-detection image is created, because transparent fill style is set expect(style.getImage()).to.not.be(style.getHitDetectionImage()); expect(style.getHitDetectionImage()).to.be.an(HTMLCanvasElement); expect(style.getHitDetectionImageSize()).to.eql([21, 21]); }); - it('creates a canvas (fill-style)', function() { + it('creates a canvas (non-transparent fill-style)', function() { const style = new CircleStyle({ radius: 10, fill: new Fill({ @@ -32,7 +50,7 @@ describe('ol.style.Circle', function() { expect(style.getImageSize()).to.eql([21, 21]); expect(style.getOrigin()).to.eql([0, 0]); expect(style.getAnchor()).to.eql([10.5, 10.5]); - // no hit-detection image is created, because fill style is set + // no hit-detection image is created, because non-transparent fill style is set expect(style.getImage()).to.be(style.getHitDetectionImage()); expect(style.getHitDetectionImage()).to.be.an(HTMLCanvasElement); expect(style.getHitDetectionImageSize()).to.eql([21, 21]); diff --git a/test/spec/ol/style/regularshape.test.js b/test/spec/ol/style/regularshape.test.js index 153b078621..aaff4b2d7d 100644 --- a/test/spec/ol/style/regularshape.test.js +++ b/test/spec/ol/style/regularshape.test.js @@ -40,13 +40,31 @@ describe('ol.style.RegularShape', function() { expect(style.getImageSize()).to.eql([21, 21]); expect(style.getOrigin()).to.eql([0, 0]); expect(style.getAnchor()).to.eql([10.5, 10.5]); - // hit-detection image is created, because no fill style is set + // no hit-detection image is created, because no fill style is set + expect(style.getImage()).to.be(style.getHitDetectionImage()); + expect(style.getHitDetectionImage()).to.be.an(HTMLCanvasElement); + expect(style.getHitDetectionImageSize()).to.eql([21, 21]); + }); + + it('creates a canvas (transparent fill-style)', function() { + const style = new RegularShape({ + radius: 10, + fill: new Fill({ + color: 'transparent' + }) + }); + expect(style.getImage()).to.be.an(HTMLCanvasElement); + expect(style.getSize()).to.eql([21, 21]); + expect(style.getImageSize()).to.eql([21, 21]); + expect(style.getOrigin()).to.eql([0, 0]); + expect(style.getAnchor()).to.eql([10.5, 10.5]); + // hit-detection image is created, because transparent fill style is set expect(style.getImage()).to.not.be(style.getHitDetectionImage()); expect(style.getHitDetectionImage()).to.be.an(HTMLCanvasElement); expect(style.getHitDetectionImageSize()).to.eql([21, 21]); }); - it('creates a canvas (fill-style)', function() { + it('creates a canvas (non-transparent fill-style)', function() { const style = new RegularShape({ radius: 10, fill: new Fill({ @@ -58,7 +76,7 @@ describe('ol.style.RegularShape', function() { expect(style.getImageSize()).to.eql([21, 21]); expect(style.getOrigin()).to.eql([0, 0]); expect(style.getAnchor()).to.eql([10.5, 10.5]); - // no hit-detection image is created, because fill style is set + // no hit-detection image is created, because non-transparent fill style is set expect(style.getImage()).to.be(style.getHitDetectionImage()); expect(style.getHitDetectionImage()).to.be.an(HTMLCanvasElement); expect(style.getHitDetectionImageSize()).to.eql([21, 21]);