diff --git a/src/ol/layer/Heatmap.js b/src/ol/layer/Heatmap.js index d0798e081f..4cbcf0a6ce 100644 --- a/src/ol/layer/Heatmap.js +++ b/src/ol/layer/Heatmap.js @@ -220,6 +220,51 @@ class Heatmap extends VectorLayer { float alpha = smoothstep(0.0, 1.0, value) * v_weight; gl_FragColor = vec4(alpha, alpha, alpha, alpha); }`, + hitVertexShader: ` + precision mediump float; + uniform mat4 u_projectionMatrix; + uniform mat4 u_offsetScaleMatrix; + uniform float u_size; + attribute vec2 a_position; + attribute float a_index; + attribute float a_weight; + attribute vec4 a_hitColor; + + varying vec2 v_texCoord; + varying float v_weight; + varying vec4 v_hitColor; + + void main(void) { + mat4 offsetMatrix = u_offsetScaleMatrix; + float offsetX = a_index == 0.0 || a_index == 3.0 ? -u_size / 2.0 : u_size / 2.0; + float offsetY = a_index == 0.0 || a_index == 1.0 ? -u_size / 2.0 : u_size / 2.0; + vec4 offsets = offsetMatrix * vec4(offsetX, offsetY, 0.0, 0.0); + gl_Position = u_projectionMatrix * vec4(a_position, 0.0, 1.0) + offsets; + float u = a_index == 0.0 || a_index == 3.0 ? 0.0 : 1.0; + float v = a_index == 0.0 || a_index == 1.0 ? 0.0 : 1.0; + v_texCoord = vec2(u, v); + v_hitColor = a_hitColor; + v_weight = a_weight; + }`, + hitFragmentShader: ` + precision mediump float; + uniform float u_blurSlope; + + varying vec2 v_texCoord; + varying float v_weight; + varying vec4 v_hitColor; + + void main(void) { + vec2 texCoord = v_texCoord * 2.0 - vec2(1.0, 1.0); + float sqRadius = texCoord.x * texCoord.x + texCoord.y * texCoord.y; + float value = (1.0 - sqrt(sqRadius)) * u_blurSlope; + float alpha = smoothstep(0.0, 1.0, value) * v_weight; + if (alpha < 0.05) { + discard; + } + + gl_FragColor = v_hitColor; + }`, uniforms: { u_size: function() { return (this.get(Property.RADIUS) + this.get(Property.BLUR)) * 2; diff --git a/test/spec/ol/layer/heatmap.test.js b/test/spec/ol/layer/heatmap.test.js index 73a5a9c5fe..713a534ebc 100644 --- a/test/spec/ol/layer/heatmap.test.js +++ b/test/spec/ol/layer/heatmap.test.js @@ -1,5 +1,9 @@ import HeatmapLayer from '../../../../src/ol/layer/Heatmap.js'; - +import Feature from '../../../../src/ol/Feature.js'; +import Point from '../../../../src/ol/geom/Point.js'; +import VectorSource from '../../../../src/ol/source/Vector.js'; +import Map from '../../../../src/ol/Map.js'; +import View from '../../../../src/ol/View.js'; describe('ol.layer.Heatmap', function() { @@ -12,4 +16,60 @@ describe('ol.layer.Heatmap', function() { }); + + describe('hit detection', function() { + + it('hit detects two distinct features', function(done) { + const target = document.createElement('div'); + target.style.width = '300px'; + target.style.height = '300px'; + document.body.appendChild(target); + + const feature = new Feature({geometry: new Point([0, 0]), id: 1, weight: 10}); + const feature2 = new Feature({geometry: new Point([14, 14]), id: 2, weight: 10}); + + const source = new VectorSource({ + features: [feature, feature2] + }); + const layer = new HeatmapLayer({ + source: source, + blur: 10, + radius: 10 + }); + const map = new Map({ + layers: [layer], + view: new View({ + center: [0, 0], + resolution: 0.1 + }), + target: target + }); + map.renderSync(); + + function hitTest(coordinate) { + const features = map.getFeaturesAtPixel( + map.getPixelFromCoordinate(coordinate) + ); + return features.length ? features[0] : null; + } + + setTimeout(function() { + let res; + + res = hitTest([0, 0]); + expect(res).to.be(feature); + res = hitTest([20, 0]); + expect(res).to.be(null); + res = hitTest([14, 14]); + expect(res).to.be(feature2); + res = hitTest([0, 14]); + expect(res).to.be(null); + + document.body.removeChild(target); + done(); + }, 100); + }); + + }); + });