├── .eslintignore ├── .npmignore ├── glsl ├── gradient.vert ├── points.frag ├── points.vert └── gradient.frag ├── test ├── .eslintrc └── test.js ├── babel.config.js ├── .eslintrc ├── .editorconfig ├── wgsl ├── points_frag.wgsl ├── gradient_vert.wgsl ├── points_vert.wgsl └── gradient_frag.wgsl ├── karma.config.js ├── .gitignore ├── LICENSE ├── .circleci └── config.yml ├── ACKNOWLEDGEMENT ├── demo ├── gl.html └── index.html ├── package.json ├── README.md ├── rollup.config.js ├── dist ├── maptalks.heatmap.es.js ├── maptalks.heatmap.js ├── maptalks.heatmap.es.js.map └── maptalks.heatmap.js.map └── index.js /.eslintignore: -------------------------------------------------------------------------------- 1 | karma.conf.js 2 | maptalks.heatlayer.min.js 3 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | /**/* 2 | !dist/*.js 3 | !dist/*.map 4 | !dist/*.css 5 | !ACKNOWLEDGEMENT 6 | -------------------------------------------------------------------------------- /glsl/gradient.vert: -------------------------------------------------------------------------------- 1 | attribute vec4 aPosition; 2 | varying vec2 texcoord; 3 | void main() { 4 | texcoord = aPosition.xy * 0.5 + 0.5; 5 | gl_Position = aPosition; 6 | } 7 | -------------------------------------------------------------------------------- /test/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "mocha": true 4 | }, 5 | "globals": { 6 | "expect" : true, 7 | "sinon": true, 8 | "happen": true 9 | }, 10 | "plugins": ["mocha"], 11 | "rules": { 12 | "no-var" : 0, 13 | "prefer-const": 0 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 'presets': [ 3 | ['@babel/env', { 4 | 'loose': true, 5 | 'modules': false 6 | }] 7 | ], 8 | 'plugins': [ 9 | ], 10 | 'ignore': [ 11 | 'dist/*.js' 12 | ], 13 | 'comments': false 14 | }; 15 | -------------------------------------------------------------------------------- /glsl/points.frag: -------------------------------------------------------------------------------- 1 | precision highp int; 2 | precision highp float; 3 | varying vec2 off, dim; 4 | varying float vIntensity; 5 | void main() { 6 | float falloff = (1.0 - smoothstep(0.0, 1.0, length(off / dim))); 7 | float intensity = falloff * vIntensity; 8 | gl_FragColor = vec4(intensity); 9 | } 10 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "env": { 4 | "browser": true, 5 | "es6": true 6 | }, 7 | "extends": "eslint:recommended", 8 | "parser": "@babel/eslint-parser", 9 | "parserOptions": { 10 | "sourceType": "module" 11 | }, 12 | "globals": { 13 | }, 14 | "rules": { 15 | "no-var" : "error", 16 | "prefer-const": "error", 17 | "no-undef": 0 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 4 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | 14 | [{package,bower}.json] 15 | indent_style = space 16 | indent_size = 2 17 | 18 | [.travis.yml] 19 | indent_style = space 20 | indent_size = 2 21 | -------------------------------------------------------------------------------- /glsl/points.vert: -------------------------------------------------------------------------------- 1 | attribute vec2 aPosition; 2 | attribute vec2 aOffset; 3 | attribute float aIntensity; 4 | 5 | varying vec2 off, dim; 6 | varying float vIntensity; 7 | 8 | uniform mat4 projViewModelMatrix; 9 | uniform float zoomScale; 10 | void main() { 11 | off = aOffset; 12 | dim = abs(off); 13 | vec2 pos = aPosition.xy + zoomScale * off; 14 | vIntensity = aIntensity / 255.0; 15 | gl_Position = projViewModelMatrix * vec4(pos, 0.0, 1.0); 16 | } 17 | -------------------------------------------------------------------------------- /wgsl/points_frag.wgsl: -------------------------------------------------------------------------------- 1 | struct FragmentInput { 2 | @location($i) off: vec2f, 3 | @location($i) dim: vec2f, 4 | @location($i) vIntensity: f32, 5 | }; 6 | 7 | @fragment 8 | fn main(fragmentInput: FragmentInput) -> @location(0) vec4f { 9 | let falloff = 1.0 - smoothstep(0.0, 1.0, length(fragmentInput.off / fragmentInput.dim)); 10 | let intensity = falloff * fragmentInput.vIntensity; 11 | return vec4f(intensity, intensity, intensity, intensity); 12 | } 13 | -------------------------------------------------------------------------------- /wgsl/gradient_vert.wgsl: -------------------------------------------------------------------------------- 1 | struct VertexInput { 2 | @location($i) aPosition: vec4i, 3 | }; 4 | 5 | struct VertexOutput { 6 | @builtin(position) Position: vec4f, 7 | @location($o) texcoord: vec2f, 8 | }; 9 | 10 | @vertex 11 | fn main(vertexInput: VertexInput) -> VertexOutput { 12 | var vertexOutput: VertexOutput; 13 | var position = vec4f(vertexInput.aPosition); 14 | 15 | vertexOutput.texcoord = vec2f(position.x * 0.5 + 0.5, -position.y * 0.5 + 0.5); 16 | vertexOutput.Position = position; 17 | 18 | return vertexOutput; 19 | } 20 | -------------------------------------------------------------------------------- /karma.config.js: -------------------------------------------------------------------------------- 1 | const pkg = require('./package.json'); 2 | 3 | module.exports = function (config) { 4 | config.set({ 5 | browserDisconnectTimeout: 100000, 6 | browserNoActivityTimeout: 100000, 7 | browserDisconnectTolerance: 12, 8 | frameworks: ['mocha', 'expect', 'expect-maptalks'], 9 | basePath: '.', 10 | client: { 11 | mocha: { 12 | timeout : 36000 13 | } 14 | }, 15 | files: [ 16 | 'node_modules/maptalks/dist/maptalks.js', 17 | pkg.main, 18 | 'test/test.js' 19 | ], 20 | proxies: { 21 | }, 22 | preprocessors: { 23 | }, 24 | browsers: ['Chrome'], 25 | reporters: ['mocha'] 26 | }); 27 | }; 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .tmp/ 2 | *.gz 3 | .idea/ 4 | *.iml 5 | .tern-port 6 | doc/ 7 | 8 | ### *tags 9 | GPATH 10 | GRTAGS 11 | GTAGS 12 | TAGS 13 | 14 | ### OSX template 15 | .DS_Store 16 | 17 | # Created by .ignore support plugin (hsz.mobi) 18 | ### Node template 19 | # Logs 20 | logs 21 | *.log 22 | 23 | # Runtime data 24 | pids 25 | *.pid 26 | *.seed 27 | 28 | # Directory for instrumented libs generated by jscoverage/JSCover 29 | lib-cov 30 | 31 | # Coverage directory used by tools like istanbul 32 | coverage 33 | 34 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 35 | .grunt 36 | 37 | # node-waf configuration 38 | .lock-wscript 39 | 40 | # Compiled binary addons (http://nodejs.org/api/addons.html) 41 | build/Release 42 | 43 | # Dependency directory 44 | # https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git 45 | node_modules 46 | 47 | #example deploy config 48 | examples/replace.json 49 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Fu Zhen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /wgsl/points_vert.wgsl: -------------------------------------------------------------------------------- 1 | struct VertexInput { 2 | @location($i) aPosition: vec2f, 3 | @location($i) aOffset: vec2f, 4 | @location($i) aIntensity: f32, 5 | }; 6 | 7 | struct VertexOutput { 8 | @builtin(position) Position: vec4f, 9 | @location($o) off: vec2f, 10 | @location($o) dim: vec2f, 11 | @location($o) vIntensity: f32, 12 | }; 13 | 14 | struct GlobalUniforms { 15 | zoomScale: f32, 16 | }; 17 | 18 | struct MyAppUniforms { 19 | projViewModelMatrix: mat4x4f, 20 | }; 21 | 22 | @group(0) @binding($b) var globalUniforms: GlobalUniforms; 23 | @group(0) @binding($b) var uniforms: MyAppUniforms; 24 | 25 | @vertex 26 | fn main(vertexInput: VertexInput) -> VertexOutput { 27 | var vertexOutput: VertexOutput; 28 | 29 | var off = vec2f(vertexInput.aOffset); 30 | vertexOutput.off = off; 31 | vertexOutput.dim = abs(vec2f(vertexInput.aOffset)); 32 | 33 | let pos = vertexInput.aPosition.xy + globalUniforms.zoomScale * off; 34 | vertexOutput.vIntensity = f32(vertexInput.aIntensity) / 255.0; 35 | 36 | vertexOutput.Position = uniforms.projViewModelMatrix * vec4f(pos, 0.0, 1.0); 37 | 38 | return vertexOutput; 39 | } 40 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | # Javascript Node CircleCI 2.0 configuration file 2 | # 3 | # Check https://circleci.com/docs/2.0/language-javascript/ for more details 4 | # 5 | version: 2 6 | jobs: 7 | build: 8 | docker: 9 | # specify the version you desire here 10 | - image: circleci/node:16.6.2-browsers 11 | environment: 12 | CHROME_BIN: "/usr/bin/google-chrome" 13 | 14 | # Specify service dependencies here if necessary 15 | # CircleCI maintains a library of pre-built images 16 | # documented at https://circleci.com/docs/2.0/circleci-images/ 17 | # - image: circleci/mongo:3.4.4 18 | 19 | working_directory: ~/repo 20 | 21 | steps: 22 | - checkout 23 | 24 | # Download and cache dependencies 25 | - restore_cache: 26 | keys: 27 | - v1-dependencies-{{ checksum "package-lock.json" }} 28 | # fallback to using the latest cache if no exact match is found 29 | - v1-dependencies- 30 | 31 | - run: npm install 32 | 33 | - save_cache: 34 | paths: 35 | - node_modules 36 | key: v1-dependencies-{{ checksum "package-lock.json" }} 37 | 38 | # run tests! 39 | - run: 40 | name: run test 41 | command: npm test 42 | no_output_timeout: 60m 43 | -------------------------------------------------------------------------------- /glsl/gradient.frag: -------------------------------------------------------------------------------- 1 | precision highp int; 2 | precision highp float; 3 | uniform sampler2D source; 4 | varying vec2 texcoord; 5 | float linstep(float low, float high, float value) { 6 | return clamp((value-low)/(high-low), 0.0, 1.0); 7 | } 8 | float fade(float low, float high, float value) { 9 | float mid = (low+high)*0.5; 10 | float range = (high-low)*0.5; 11 | float x = 1.0 - clamp(abs(mid-value)/range, 0.0, 1.0); 12 | return smoothstep(0.0, 1.0, x); 13 | } 14 | vec3 getColor(float intensity) { 15 | vec3 blue = vec3(0.0, 0.0, 1.0); 16 | vec3 cyan = vec3(0.0, 1.0, 1.0); 17 | vec3 green = vec3(0.0, 1.0, 0.0); 18 | vec3 yellow = vec3(1.0, 1.0, 0.0); 19 | vec3 red = vec3(1.0, 0.0, 0.0); 20 | vec3 color = ( 21 | fade(-0.25, 0.25, intensity)*blue + 22 | fade(0.0, 0.5, intensity)*cyan + 23 | fade(0.25, 0.75, intensity)*green + 24 | fade(0.5, 1.0, intensity)*yellow + 25 | smoothstep(0.75, 1.0, intensity)*red 26 | ); 27 | return color; 28 | } 29 | vec4 alphaFun(vec3 color, float intensity) { 30 | float alpha = smoothstep(0.00000000, 1.00000000, intensity); 31 | return vec4(color*alpha, alpha); 32 | } 33 | void main() { 34 | vec4 value = texture2D(source, texcoord); 35 | float intensity = smoothstep(0.0, 1.0, value.r); 36 | vec3 color = getColor(intensity); 37 | gl_FragColor = alphaFun(color, intensity); 38 | // gl_FragColor = value; 39 | } 40 | -------------------------------------------------------------------------------- /ACKNOWLEDGEMENT: -------------------------------------------------------------------------------- 1 | Contains code from simpleheat 2 | 3 | Copyright (c) 2015, Vladimir Agafonkin 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without modification, are 7 | permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this list of 10 | conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, this list 13 | of conditions and the following disclaimer in the documentation and/or other materials 14 | provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 17 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 18 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 19 | COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 20 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 21 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 23 | TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /demo/gl.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | maptalks.heatmap demo 5 | 6 | 7 | 8 | 9 | 17 | 18 | 19 |
20 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /wgsl/gradient_frag.wgsl: -------------------------------------------------------------------------------- 1 | struct FragmentInput { 2 | @location($i) texcoord: vec2f, 3 | }; 4 | 5 | 6 | @group(0) @binding($b) var source: texture_2d; 7 | @group(0) @binding($b) var sourceSampler: sampler; 8 | 9 | fn linstep(low: f32, high: f32, value: f32) -> f32 { 10 | return clamp((value - low) / (high - low), 0.0, 1.0); 11 | } 12 | 13 | fn fade(low: f32, high: f32, value: f32) -> f32 { 14 | let mid = (low + high) * 0.5; 15 | let range = (high - low) * 0.5; 16 | let x = 1.0 - clamp(abs(mid - value) / range, 0.0, 1.0); 17 | return smoothstep(0.0, 1.0, x); 18 | } 19 | 20 | fn getColor(intensity: f32) -> vec3f { 21 | let blue = vec3f(0.0, 0.0, 1.0); 22 | let cyan = vec3f(0.0, 1.0, 1.0); 23 | let green = vec3f(0.0, 1.0, 0.0); 24 | let yellow = vec3f(1.0, 1.0, 0.0); 25 | let red = vec3f(1.0, 0.0, 0.0); 26 | 27 | let color = ( 28 | fade(-0.25, 0.25, intensity) * blue + 29 | fade(0.0, 0.5, intensity) * cyan + 30 | fade(0.25, 0.75, intensity) * green + 31 | fade(0.5, 1.0, intensity) * yellow + 32 | smoothstep(0.75, 1.0, intensity) * red 33 | ); 34 | return color; 35 | } 36 | 37 | fn alphaFun(color: vec3f, intensity: f32) -> vec4f { 38 | let alpha = smoothstep(0.00000000, 1.00000000, intensity); 39 | return vec4f(color * alpha, alpha); 40 | } 41 | 42 | @fragment 43 | fn main(fragmentInput: FragmentInput) -> @location(0) vec4f { 44 | let value = textureSample(source, sourceSampler, fragmentInput.texcoord); 45 | let intensity = smoothstep(0.0, 1.0, value.r); 46 | let color = getColor(intensity); 47 | let fragColor = alphaFun(color, intensity); 48 | return fragColor; 49 | } 50 | -------------------------------------------------------------------------------- /demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | maptalks.heatmap demo 6 | 7 | 8 | 9 | 10 | 23 | 24 | 25 | 26 |
27 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "maptalks.heatmap", 3 | "version": "0.7.0", 4 | "description": "A heatmap layer plugin for maptalks.js. ", 5 | "license": "MIT", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/maptalks/maptalks.heatmap.git" 9 | }, 10 | "main": "dist/maptalks.heatmap.js", 11 | "module": "dist/maptalks.heatmap.es.js", 12 | "jsnext:main": "dist/maptalks.heatmap.es.js", 13 | "scripts": { 14 | "dev": "rollup -w -c rollup.config.js", 15 | "build": "rollup --environment BUILD:production -c rollup.config.js", 16 | "build-dev": "rollup -c rollup.config.js", 17 | "test": "karma start --single-run", 18 | "tdd": "karma start --no-single-run", 19 | "preversion": "npm run lint", 20 | "version": "npm run build", 21 | "lint": "eslint index.js test/**/*.js", 22 | "prepare": "npm run lint && npm run build" 23 | }, 24 | "devDependencies": { 25 | "@babel/core": "^7.15.0", 26 | "@babel/eslint-parser": "^7.15.0", 27 | "@babel/preset-env": "^7.15.0", 28 | "@rollup/plugin-commonjs": "^20.0.0", 29 | "@rollup/plugin-json": "^4.1.0", 30 | "@rollup/plugin-node-resolve": "^13.0.4", 31 | "@rollup/plugin-replace": "4.0.0", 32 | "expect.js": "^0.3.1", 33 | "eslint": "^7.32.0", 34 | "eslint-plugin-mocha": "^9.0.0", 35 | "karma": "^6.4.0", 36 | "karma-chrome-launcher": "^3.1.1", 37 | "karma-expect": "^1.1.3", 38 | "karma-mocha": "^2.0.1", 39 | "karma-mocha-reporter": "^2.2.5", 40 | "minimist": "^1.2.0", 41 | "mocha": "^5.2.0", 42 | "rollup": "^2.56.2", 43 | "rollup-plugin-terser": "^7.0.2" 44 | }, 45 | "dependencies": { 46 | "simpleheat": "^0.4.0", 47 | "@maptalks/gl": ">=0.110.0" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # maptalks.heatmap 2 | 3 | [![CircleCI](https://circleci.com/gh/maptalks/maptalks.heatmap/tree/master.svg?style=shield)](https://circleci.com/gh/maptalks/maptalks.heatmap) 4 | [![NPM Version](https://img.shields.io/npm/v/maptalks.heatmap.svg)](https://github.com/maptalks/maptalks.heatmap) 5 | 6 | A plugin of [maptalks.js](https://github.com/maptalks/maptalks.js) to draw heatmap on maps, based on mourner's [simpleheat](https://github.com/mourner/simpleheat). 7 | 8 | ![screenshot](https://cloud.githubusercontent.com/assets/13678919/25303099/98ad71fa-277e-11e7-8722-a3435d11e1f5.jpg) 9 | 10 | ## Examples 11 | 12 | * A heatmap of [50000 points](https://maptalks.github.io/maptalks.heatmap/demo/). (data from [Leaflet.Heat](https://github.com/Leaflet/Leaflet.heat)) 13 | * Heatmap of [50000 points](https://maptalks.github.io/maptalks.heatmap/demo/gl.html) rendered by WebGL (with maptalks-gl) 14 | 15 | ## Install 16 | 17 | * Install with npm: ```npm install maptalks.heatmap```. 18 | * Download from [dist directory](https://github.com/maptalks/maptalks.heatmap/tree/gh-pages/dist). 19 | * Use unpkg CDN: ```https://unpkg.com/maptalks.heatmap/dist/maptalks.heatmap.min.js``` 20 | 21 | ## Usage 22 | 23 | As a plugin, ```maptalks.heatmap``` must be loaded after ```maptalks.js``` in browsers. 24 | 25 | ### Vanilla Javascript 26 | ```html 27 | 28 | 29 | 33 | ``` 34 | 35 | ### ES6 36 | 37 | ```javascript 38 | import { HeatLayer } from 'maptalks.heatmap'; 39 | const data = [[0, 0, 0.3], [0, 0, 0.4], [0, 0, 0.4]]; 40 | const heatLayer = new HeatLayer('heat', data).addTo(map); 41 | ``` 42 | 43 | ## Supported Browsers 44 | 45 | IE 9-11, Chrome, Firefox, other modern and mobile browsers. 46 | 47 | ## API Reference 48 | 49 | ### `Constructor` 50 | 51 | ```HeatmapLayer``` is a subclass of [maptalks.Layer](https://maptalks.github.io/maptalks.js/api/0.x/Layer.html) and inherits all the methods of its parent. 52 | 53 | ```javascript 54 | new maptalks.HeatmapLayer(id, data, options) 55 | ``` 56 | 57 | * id **String** layer id 58 | * data **Array[]** layer data: [[x, y, value], [x, y, value]..] 59 | * options **Object** options 60 | * max **Number** max data value (1 by default) 61 | * radius **Number** point radius(25 by default) 62 | * blur **Number** blur radius(15 by default) 63 | * minOpacity **Number** minimum point opacity (0.05 by default) 64 | * heatValueScale **Number** the scale value to multiply with heat data (1 by default) 65 | * gradient **Object** set gradient colors as {\: '\'}, default by { 0.4: 'blue', 0.6: 'cyan', 0.7: 'lime', 0.8: 'yellow', 1.0: 'red' } 66 | * Other options defined in [maptalks.Layer](https://maptalks.github.io/maptalks.js/api/0.x/Layer.html) 67 | 68 | ### `config(key, value)` 69 | 70 | config layer's options and redraw the layer if necessary 71 | 72 | ```javascript 73 | heatLayer.config('max', 10); 74 | heatLayer.config({ 75 | 'radius' : 80, 76 | 'blur' : 30, 77 | 'gradient' : {0.4: 'blue', 0.65: 'lime', 1: 'red'} 78 | }); 79 | ``` 80 | 81 | **Returns** `this` 82 | 83 | ### `getData` 84 | 85 | get layer's data 86 | 87 | **Returns** `Array[]` 88 | 89 | ### `setData(data)` 90 | 91 | set new data 92 | 93 | * data **Array[]** data to set 94 | 95 | **Returns** `this` 96 | 97 | ### `addPoint(point)` 98 | 99 | add more points 100 | 101 | * point **Array[]** points to add, [[x, y, value], [x, y, value]..] 102 | 103 | **Returns** `this` 104 | 105 | ### `redraw()` 106 | 107 | **Returns** `this` 108 | 109 | ### `isEmpty()` 110 | 111 | **Returns** `Boolean` 112 | 113 | ### `clear()` 114 | 115 | **Returns** `this` 116 | 117 | ### `toJSON(options)` 118 | 119 | export the layer's JSON. 120 | 121 | * options **Object** options 122 | * clipExtent **maptalks.Extent** the extent to clip 123 | 124 | ```javascript 125 | // only export points in map's current extent. 126 | heatLayer.toJSON({ 127 | 'clipExtent' : map.getExtent() 128 | }); 129 | ``` 130 | 131 | **Returns** `Object` 132 | 133 | ## Contributing 134 | 135 | We welcome any kind of contributions including issue reportings, pull requests, documentation corrections, feature requests and any other helps. 136 | 137 | ## Develop 138 | 139 | The only source file is ```index.js```. 140 | 141 | It is written in ES6, transpiled by [babel](https://babeljs.io/) and tested with [mocha](https://mochajs.org) and [expect.js](https://github.com/Automattic/expect.js). 142 | 143 | ### Scripts 144 | 145 | * Install dependencies 146 | ```shell 147 | $ npm install 148 | ``` 149 | 150 | * Watch source changes and generate runnable bundle repeatedly 151 | ```shell 152 | $ gulp watch 153 | ``` 154 | 155 | * Tests 156 | ```shell 157 | $ npm test 158 | ``` 159 | 160 | * Watch source changes and run tests repeatedly 161 | ```shell 162 | $ gulp tdd 163 | ``` 164 | 165 | * Package and generate minified bundles to dist directory 166 | ```shell 167 | $ gulp minify 168 | ``` 169 | 170 | * Lint 171 | ```shell 172 | $ npm run lint 173 | ``` 174 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | const { nodeResolve: resolve } = require('@rollup/plugin-node-resolve'); 2 | const commonjs = require('@rollup/plugin-commonjs'); 3 | const terser = require('rollup-plugin-terser').terser; 4 | const pkg = require('./package.json'); 5 | 6 | 7 | function glsl() { 8 | return { 9 | transform(code, id) { 10 | if (/\.vert$/.test(id) === false && /\.frag$/.test(id) === false && /\.glsl$/.test(id) === false) return null; 11 | let transformedCode = JSON.stringify(code.trim() 12 | .replace(/\r/g, '') 13 | .replace(/[ \t]*\/\/.*\n/g, '') // remove // 14 | .replace(/[ \t]*\/\*[\s\S]*?\*\//g, '') // remove /* */ 15 | .replace(/\n{2,}/g, '\n')); // # \n+ to \n;; 16 | transformedCode = `export default ${transformedCode};`; 17 | return { 18 | code: transformedCode, 19 | map: { mappings: '' } 20 | }; 21 | } 22 | }; 23 | } 24 | 25 | function wgsl() { 26 | return { 27 | transform(code, id) { 28 | if (/\.wgsl$/.test(id) === false) return null; 29 | let transformedCode = JSON.stringify(code.trim() 30 | // .replace(/(^\s*)|(\s*$)/gm, '') 31 | .replace(/\r/g, '') 32 | .replace(/[ \t]*\/\/.*\n/g, '') // remove // 33 | .replace(/[ \t]*\/\*[\s\S]*?\*\//g, '') // remove /* */ 34 | .replace(/\n{2,}/g, '\n')); // # \n+ to \n;; 35 | transformedCode = `export default ${transformedCode};`; 36 | return { 37 | code: transformedCode, 38 | map: { mappings: '' } 39 | }; 40 | } 41 | }; 42 | } 43 | 44 | 45 | const production = process.env.BUILD === 'production'; 46 | const outputFile = pkg.main; 47 | const outputESFile = pkg.module; 48 | const plugins = [ 49 | ].concat(production ? [ 50 | removeGlobal(), 51 | terser({ 52 | output : { 53 | keep_quoted_props: true, 54 | beautify: false, 55 | comments : '/^!/' 56 | } 57 | }) 58 | ] : []); 59 | 60 | //worker.js中的global可能被webpack替换为全局变量,造成worker代码执行失败,所以这里统一把typeof global替换为typeof undefined 61 | function removeGlobal() { 62 | return { 63 | transform(code, id) { 64 | if (id.indexOf('worker.js') === -1) return null; 65 | const commonjsCode = /typeof global/g; 66 | const transformedCode = code.replace(commonjsCode, 'typeof undefined'); 67 | return { 68 | code: transformedCode, 69 | map: { mappings: '' } 70 | }; 71 | } 72 | }; 73 | } 74 | 75 | const banner = `/*!\n * ${pkg.name} v${pkg.version}\n * LICENSE : ${pkg.license}\n * (c) 2016-${new Date().getFullYear()} maptalks.org\n */`; 76 | 77 | let outro = pkg.name + ' v' + pkg.version; 78 | if (pkg.peerDependencies && pkg.peerDependencies['maptalks']) { 79 | outro += `, requires maptalks@${pkg.peerDependencies.maptalks}.`; 80 | } 81 | 82 | outro = `typeof console !== 'undefined' && console.log('${outro}');`; 83 | 84 | module.exports = [ 85 | { 86 | input: 'index.js', 87 | plugins: [ 88 | glsl(), 89 | wgsl(), 90 | resolve({ 91 | browser: true, 92 | preferBuiltins: false 93 | }), 94 | commonjs({ 95 | // global keyword handling causes Webpack compatibility issues, so we disabled it: 96 | // https://github.com/mapbox/mapbox-gl-js/pull/6956 97 | ignoreGlobal: true 98 | }) 99 | ].concat(plugins), 100 | external: ['maptalks', '@maptalks/gl'], 101 | output: { 102 | globals: { 103 | 'maptalks': 'maptalks', 104 | '@maptalks/gl': 'maptalks' 105 | }, 106 | banner, 107 | outro, 108 | extend: true, 109 | name: 'maptalks', 110 | file: outputFile, 111 | format: 'umd', 112 | sourcemap: true, 113 | }, 114 | watch: { 115 | include: ['index.js', '**/*/*.vert', '**/*/*.frag', '**/*/*.wgsl'] 116 | } 117 | }, 118 | { 119 | input: 'index.js', 120 | plugins: [ 121 | glsl(), 122 | wgsl(), 123 | resolve({ 124 | browser: true, 125 | preferBuiltins: false 126 | }), 127 | commonjs({ 128 | // global keyword handling causes Webpack compatibility issues, so we disabled it: 129 | // https://github.com/mapbox/mapbox-gl-js/pull/6956 130 | ignoreGlobal: true 131 | }) 132 | ].concat(plugins), 133 | external: ['maptalks', '@maptalks/gl'], 134 | output: { 135 | globals: { 136 | 'maptalks': 'maptalks', 137 | '@maptalks/gl': 'maptalks' 138 | }, 139 | banner, 140 | outro, 141 | extend: true, 142 | name: 'maptalks', 143 | file: outputESFile, 144 | format: 'es', 145 | sourcemap: true, 146 | }, 147 | watch: { 148 | include: ['index.js'] 149 | } 150 | } 151 | ]; 152 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | describe('Layer', function () { 2 | var container, map; 3 | beforeEach(function () { 4 | container = document.createElement('div'); 5 | container.style.width = '400px'; 6 | container.style.height = '300px'; 7 | document.body.appendChild(container); 8 | map = new maptalks.Map(container, { 9 | center : [0, 0], 10 | zoom : 17 11 | }); 12 | }); 13 | 14 | afterEach(function () { 15 | map.remove(); 16 | maptalks.DomUtil.removeDomNode(container); 17 | }); 18 | 19 | //heat data to test 20 | var data = [ 21 | //x, y, heat 22 | [0, 0, 1], 23 | [0, 0, 2], 24 | [0, 0, 3] 25 | ]; 26 | 27 | it('should display when added to map', function (done) { 28 | var layer = new maptalks.HeatLayer('g', data); 29 | layer.on('layerload', function () { 30 | expect(layer).to.be.painted(); 31 | done(); 32 | }) 33 | .addTo(map); 34 | }); 35 | 36 | it('should display if added again after removed', function (done) { 37 | var layer = new maptalks.HeatLayer('g', data); 38 | layer.once('layerload', function () { 39 | expect(layer).to.be.painted(); 40 | map.removeLayer(layer); 41 | layer.once('layerload', function () { 42 | expect(layer).to.be.painted(); 43 | done(); 44 | }); 45 | map.addLayer(layer); 46 | }); 47 | map.addLayer(layer); 48 | }); 49 | 50 | it('should display after zooming', function (done) { 51 | var layer = new maptalks.HeatLayer('g', data); 52 | layer.once('layerload', function () { 53 | map.on('zoomend', function () { 54 | map.on('frameend', function () { 55 | expect(layer).to.be.painted(); 56 | done(); 57 | }); 58 | }); 59 | map.zoomIn(); 60 | }); 61 | map.addLayer(layer); 62 | }); 63 | 64 | it('should show', function (done) { 65 | var layer = new maptalks.HeatLayer('g', data, { visible : false }); 66 | layer.once('add', function () { 67 | expect(layer).not.to.be.painted(); 68 | layer.once('layerload', function () { 69 | expect(layer).to.be.painted(); 70 | done(); 71 | }); 72 | layer.show(); 73 | }); 74 | map.addLayer(layer); 75 | }); 76 | 77 | it('should hide', function (done) { 78 | var layer = new maptalks.HeatLayer('g', data); 79 | layer.once('layerload', function () { 80 | expect(layer).to.be.painted(); 81 | layer.once('hide', function () { 82 | expect(layer).not.to.be.painted(); 83 | done(); 84 | }); 85 | layer.hide(); 86 | }); 87 | map.addLayer(layer); 88 | }); 89 | 90 | it('should serialized with JSON', function (done) { 91 | var layer = new maptalks.HeatLayer('g', data); 92 | var json = layer.toJSON(); 93 | var copy = maptalks.Layer.fromJSON(json); 94 | expect(data).to.be.eql(copy.getData()); 95 | copy.on('layerload', function () { 96 | expect(copy).to.be.painted(); 97 | done(); 98 | }) 99 | .addTo(map); 100 | }); 101 | 102 | it('should can add point', function (done) { 103 | var layer = new maptalks.HeatLayer('g'); 104 | layer.once('add', function () { 105 | expect(layer).not.to.be.painted(); 106 | layer.once('layerload', function () { 107 | expect(layer).to.be.painted(); 108 | done(); 109 | }); 110 | layer.addPoint([0, 0, 1]); 111 | }) 112 | .addTo(map); 113 | }); 114 | 115 | it('should can set data', function (done) { 116 | var layer = new maptalks.HeatLayer('g'); 117 | layer.once('add', function () { 118 | expect(layer).not.to.be.painted(); 119 | layer.once('layerload', function () { 120 | expect(layer).to.be.painted(); 121 | done(); 122 | }); 123 | layer.setData([[0, 0, 1]]); 124 | }) 125 | .addTo(map); 126 | }); 127 | 128 | it('should update when setting data', function (done) { 129 | var layer = new maptalks.HeatLayer('g'); 130 | layer.once('layerload', function () { 131 | expect(layer).to.be.painted(0, 0, [255, 2, 0]); 132 | layer.once('layerload', function () { 133 | expect(layer).to.be.painted(0, 0, [255, 2, 0]); 134 | done(); 135 | }); 136 | layer.setData([[0, 0, 5], [0, 0, 5]]); 137 | }) 138 | .setData([[0, 0, 2]]) 139 | .addTo(map); 140 | }); 141 | 142 | it('should update when adding more points', function (done) { 143 | var layer = new maptalks.HeatLayer('g'); 144 | layer.once('layerload', function () { 145 | expect(layer).to.be.painted(0, 0, [255, 2, 0]); 146 | layer.once('layerload', function () { 147 | expect(layer).to.be.painted(0, 0, [255, 2, 0]); 148 | done(); 149 | }); 150 | layer.addPoint([[0, 0, 5], [0, 0, 5]]); 151 | }) 152 | .setData([[0, 0, 2]]) 153 | .addTo(map); 154 | }); 155 | 156 | it('should update immediately with drawImmediate options', function () { 157 | var layer = new maptalks.HeatLayer('g', { 158 | 'drawImmediate' : true 159 | }) 160 | .setData([[0, 0, 2]]) 161 | .addTo(map); 162 | layer.once('layerload', function () { 163 | expect(layer).to.be.painted(0, 0, [0, 129, 255]); 164 | layer.setData([[0, 0, 5], [0, 0, 5]]); 165 | expect(layer).to.be.painted(0, 0, [255, 3, 0]); 166 | }); 167 | }); 168 | }); 169 | -------------------------------------------------------------------------------- /dist/maptalks.heatmap.es.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * maptalks.heatmap v0.7.0 3 | * LICENSE : MIT 4 | * (c) 2016-2025 maptalks.org 5 | */ 6 | import*as t from"maptalks";import{CanvasCompatible as e,reshader as i}from"@maptalks/gl";var n={exports:{}};!function(t){function e(t){if(!(this instanceof e))return new e(t);this._canvas=t="string"==typeof t?document.getElementById(t):t,this._ctx=t.getContext("2d"),this._width=t.width,this._height=t.height,this._max=1,this._data=[]}n.exports=e,e.prototype={defaultRadius:25,defaultGradient:{.4:"blue",.6:"cyan",.7:"lime",.8:"yellow",1:"red"},data:function(t){return this._data=t,this},max:function(t){return this._max=t,this},add:function(t){return this._data.push(t),this},clear:function(){return this._data=[],this},radius:function(t,e){e=void 0===e?15:e;var i=this._circle=this._createCanvas(),n=i.getContext("2d"),a=this._r=t+e;return i.width=i.height=2*a,n.shadowOffsetX=n.shadowOffsetY=2*a,n.shadowBlur=e,n.shadowColor="black",n.beginPath(),n.arc(-a,-a,t,0,2*Math.PI,!0),n.closePath(),n.fill(),this},resize:function(){this._width=this._canvas.width,this._height=this._canvas.height},gradient:function(t){var e=this._createCanvas(),i=e.getContext("2d"),n=i.createLinearGradient(0,0,0,256);for(var a in e.width=1,e.height=256,t)n.addColorStop(+a,t[a]);return i.fillStyle=n,i.fillRect(0,0,1,256),this._grad=i.getImageData(0,0,1,256).data,this},draw:function(t){this._circle||this.radius(this.defaultRadius),this._grad||this.gradient(this.defaultGradient);var e=this._ctx;e.clearRect(0,0,this._width,this._height);for(var i,n=0,a=this._data.length;nt._pointToContainerPoint(e))),!n.intersects(i))return void this.completeRender();s=i.intersection(n)}this._heater||(this._heater=a(this.canvas)),this._heater.radius(e.options.radius||this._heater.defaultRadius,e.options.blur),e.options.gradient&&this._heater.gradient(e.options.gradient),this._heater.max(e.options.max),this._heatViews||(this._heatViews=[]);const r=e.getData();if(0===r.length)return void this.completeRender();if(this._heater){const t=this._heater._width,e=this._heater._height;if(Math.min(t,e)<=0)return void this.completeRender()}const o=this._heatData(r,s);this._heater.data(o).draw(e.options.minOpacity),this.completeRender()}drawOnInteracting(){this.draw()}_heatData(e,i){const n=this.getMap(),a=this.layer,s=n.getProjection(),r=[],o=this._heater._r,h=void 0===a.options.max?1:a.options.max,f=o/2,d=[],l=n.offsetPlatform(),u=Math.abs(l.x)%f,c=Math.abs(l.y)%f;let p,g,_,m,v,y;const{xmin:x,ymin:w,xmax:D,ymax:b}=i.expand(o);this._heatRadius=o;const I=new t.Coordinate(0,0);for(let t=0,i=e.length;tD||g.yb||(m=Math.floor((g.x-u)/f)+2,v=Math.floor((g.y-c)/f)+2,y=(void 0!==p[2]?+p[2]:.1)*a.options.heatValueScale,d[v]=d[v]||[],_=d[v][m],_?(_[0]=(_[0]*_[2]+g.x*y)/(_[2]+y),_[1]=(_[1]*_[2]+g.y*y)/(_[2]+y),_[2]+=y):d[v][m]=[g.x,g.y,y]);for(let t=0,e=d.length;tthis.canvas?this.canvas.width:1,height:()=>this.canvas?this.canvas.height:1},e={blend:{enable:!0,func:{dst:1,src:1}},depth:{enable:!1},viewport:t};this._pointShader=new i.MeshShader({name:"heatmap-point",vert:"attribute vec2 aPosition;\nattribute vec2 aOffset;\nattribute float aIntensity;\nvarying vec2 off, dim;\nvarying float vIntensity;\nuniform mat4 projViewModelMatrix;\nuniform float zoomScale;\nvoid main() {\n off = aOffset;\n dim = abs(off);\n vec2 pos = aPosition.xy + zoomScale * off;\n vIntensity = aIntensity / 255.0;\n gl_Position = projViewModelMatrix * vec4(pos, 0.0, 1.0);\n}",frag:"precision highp int;\nprecision highp float;\nvarying vec2 off, dim;\nvarying float vIntensity;\nvoid main() {\n float falloff = (1.0 - smoothstep(0.0, 1.0, length(off / dim)));\n float intensity = falloff * vIntensity;\n gl_FragColor = vec4(intensity);\n}",wgslVert:"struct VertexInput {\n @location($i) aPosition: vec2f,\n @location($i) aOffset: vec2f,\n @location($i) aIntensity: f32,\n};\nstruct VertexOutput {\n @builtin(position) Position: vec4f,\n @location($o) off: vec2f,\n @location($o) dim: vec2f,\n @location($o) vIntensity: f32,\n};\nstruct GlobalUniforms {\n zoomScale: f32,\n};\nstruct MyAppUniforms {\n projViewModelMatrix: mat4x4f,\n};\n@group(0) @binding($b) var globalUniforms: GlobalUniforms;\n@group(0) @binding($b) var uniforms: MyAppUniforms;\n@vertex\nfn main(vertexInput: VertexInput) -> VertexOutput {\n var vertexOutput: VertexOutput;\n var off = vec2f(vertexInput.aOffset);\n vertexOutput.off = off;\n vertexOutput.dim = abs(vec2f(vertexInput.aOffset));\n let pos = vertexInput.aPosition.xy + globalUniforms.zoomScale * off;\n vertexOutput.vIntensity = f32(vertexInput.aIntensity) / 255.0;\n vertexOutput.Position = uniforms.projViewModelMatrix * vec4f(pos, 0.0, 1.0);\n return vertexOutput;\n}",wgslFrag:"struct FragmentInput {\n @location($i) off: vec2f,\n @location($i) dim: vec2f,\n @location($i) vIntensity: f32,\n};\n@fragment\nfn main(fragmentInput: FragmentInput) -> @location(0) vec4f {\n let falloff = 1.0 - smoothstep(0.0, 1.0, length(fragmentInput.off / fragmentInput.dim));\n let intensity = falloff * fragmentInput.vIntensity;\n return vec4f(intensity, intensity, intensity, intensity);\n}",extraCommandProps:e}),this._gradientShader=new i.MeshShader({name:"heatmap-gradient",vert:"attribute vec4 aPosition;\nvarying vec2 texcoord;\nvoid main() {\n texcoord = aPosition.xy * 0.5 + 0.5;\n gl_Position = aPosition;\n}",frag:"precision highp int;\nprecision highp float;\nuniform sampler2D source;\nvarying vec2 texcoord;\nfloat linstep(float low, float high, float value) {\n return clamp((value-low)/(high-low), 0.0, 1.0);\n}\nfloat fade(float low, float high, float value) {\n float mid = (low+high)*0.5;\n float range = (high-low)*0.5;\n float x = 1.0 - clamp(abs(mid-value)/range, 0.0, 1.0);\n return smoothstep(0.0, 1.0, x);\n}\nvec3 getColor(float intensity) {\n vec3 blue = vec3(0.0, 0.0, 1.0);\n vec3 cyan = vec3(0.0, 1.0, 1.0);\n vec3 green = vec3(0.0, 1.0, 0.0);\n vec3 yellow = vec3(1.0, 1.0, 0.0);\n vec3 red = vec3(1.0, 0.0, 0.0);\n vec3 color = (\n fade(-0.25, 0.25, intensity)*blue +\n fade(0.0, 0.5, intensity)*cyan +\n fade(0.25, 0.75, intensity)*green +\n fade(0.5, 1.0, intensity)*yellow +\n smoothstep(0.75, 1.0, intensity)*red\n );\n return color;\n}\nvec4 alphaFun(vec3 color, float intensity) {\n float alpha = smoothstep(0.00000000, 1.00000000, intensity);\n return vec4(color*alpha, alpha);\n}\nvoid main() {\n vec4 value = texture2D(source, texcoord);\n float intensity = smoothstep(0.0, 1.0, value.r);\n vec3 color = getColor(intensity);\n gl_FragColor = alphaFun(color, intensity);\n}",wgslVert:"struct VertexInput {\n @location($i) aPosition: vec4i,\n};\nstruct VertexOutput {\n @builtin(position) Position: vec4f,\n @location($o) texcoord: vec2f,\n};\n@vertex\nfn main(vertexInput: VertexInput) -> VertexOutput {\n var vertexOutput: VertexOutput;\n var position = vec4f(vertexInput.aPosition);\n vertexOutput.texcoord = vec2f(position.x * 0.5 + 0.5, -position.y * 0.5 + 0.5);\n vertexOutput.Position = position;\n return vertexOutput;\n}",wgslFrag:"struct FragmentInput {\n @location($i) texcoord: vec2f,\n};\n@group(0) @binding($b) var source: texture_2d;\n@group(0) @binding($b) var sourceSampler: sampler;\nfn linstep(low: f32, high: f32, value: f32) -> f32 {\n return clamp((value - low) / (high - low), 0.0, 1.0);\n}\nfn fade(low: f32, high: f32, value: f32) -> f32 {\n let mid = (low + high) * 0.5;\n let range = (high - low) * 0.5;\n let x = 1.0 - clamp(abs(mid - value) / range, 0.0, 1.0);\n return smoothstep(0.0, 1.0, x);\n}\nfn getColor(intensity: f32) -> vec3f {\n let blue = vec3f(0.0, 0.0, 1.0);\n let cyan = vec3f(0.0, 1.0, 1.0);\n let green = vec3f(0.0, 1.0, 0.0);\n let yellow = vec3f(1.0, 1.0, 0.0);\n let red = vec3f(1.0, 0.0, 0.0);\n let color = (\n fade(-0.25, 0.25, intensity) * blue +\n fade(0.0, 0.5, intensity) * cyan +\n fade(0.25, 0.75, intensity) * green +\n fade(0.5, 1.0, intensity) * yellow +\n smoothstep(0.75, 1.0, intensity) * red\n );\n return color;\n}\nfn alphaFun(color: vec3f, intensity: f32) -> vec4f {\n let alpha = smoothstep(0.00000000, 1.00000000, intensity);\n return vec4f(color * alpha, alpha);\n}\n@fragment\nfn main(fragmentInput: FragmentInput) -> @location(0) vec4f {\n let value = textureSample(source, sourceSampler, fragmentInput.texcoord);\n let intensity = smoothstep(0.0, 1.0, value.r);\n let color = getColor(intensity);\n let fragColor = alphaFun(color, intensity);\n return fragColor;\n}",extraCommandProps:{blend:{enable:!0,func:{src:1,dst:"one minus src alpha"}},depth:{enable:!1},viewport:t}})}_initData(){this.bufferIndex=0,this.offsetIndex=0,this.intensityIndex=0,this.pointCount=0,this.maxPointCount=10240;const{positionBufferData:t,offsetBufferData:e,intensityBufferData:i}=this._initBuffers();this.positionBufferData=t,this.offsetBufferData=e,this.intensityBufferData=i}_initBuffers(){const t=this.getMap().getRenderer().isWebGPU();return{positionBufferData:new Float32Array(2*this.maxPointCount*6),offsetBufferData:new Float32Array(2*this.maxPointCount*6),intensityBufferData:t?new Float32Array(1*this.maxPointCount*6):new Uint8Array(1*this.maxPointCount*6)}}_initMesh(){this._renderer=new i.Renderer(this.device),this._geometry=new i.Geometry({aPosition:this.positionBufferData,aOffset:this.offsetBufferData,aIntensity:this.intensityBufferData},null,6*this.pointCount,{positionSize:2}),this._geometry.generateBuffers(this.device),this._mesh=new i.Mesh(this._geometry),this._scene=new i.Scene([this._mesh]);const t=this.canvas,e=this.device.texture({type:"float16",min:"nearest",mag:"nearest",width:t.width,height:t.height});this._fbo=this.device.framebuffer({width:t.width,height:t.height,colors:[e],colorFormat:"rgba",depthStencil:!1});const n=new Int8Array([-1,-1,0,1,1,-1,0,1,-1,1,0,1,-1,1,0,1,1,-1,0,1,1,1,0,1]);this._gradientGeometry=new i.Geometry({aPosition:n},null,0,{positionSize:4}),this._gradientGeometry.generateBuffers(this.device),this._gradientMesh=new i.Mesh(this._gradientGeometry),this._gradientScene=new i.Scene([this._gradientMesh])}_initGradient(){const t=function(t){const e=document.createElement("canvas"),i=e.getContext("2d",{willReadFrequently:!0}),n=i.createLinearGradient(0,0,0,256);e.width=256,e.height=1;for(const e in t)n.addColorStop(+e,t[e]);return i.fillStyle=n,i.fillRect(0,0,1,256),{data:i.getImageData(0,0,1,256).data,width:e.width,height:e.height}}(this.layer.options.gradient);this._gradientTexture?this._gradientTexture.update?this._gradientTexture.update(t):this._gradientTexture(t):this._gradientTexture=this.device.texture(t),this._gradientMesh.setUniform("source",this._fbo)}addVertex(t,e,i,n,a){const o=this.getMap(),h=o.getGLRes();s.set(t,e);const f=o.coordToPointAtRes(s,h,r);t=f.x,e=f.y,this.positionBufferData[this.bufferIndex++]=t,this.positionBufferData[this.bufferIndex++]=e,this.offsetBufferData[this.offsetIndex++]=i,this.offsetBufferData[this.offsetIndex++]=n,this.intensityBufferData[this.intensityIndex++]=255*a}addPoint(t,e,i){const n=this.layer.options.radius||25;null==i&&(i=.2);i/=this.layer.options.max||1,this._check();const a=n;return this.addVertex(t,e,-a,-a,i),this.addVertex(t,e,+a,-a,i),this.addVertex(t,e,-a,+a,i),this.addVertex(t,e,-a,+a,i),this.addVertex(t,e,+a,-a,i),this.addVertex(t,e,+a,+a,i),this.pointCount+=1}_check(){if(this.pointCount>=this.maxPointCount-1){this.maxPointCount+=10240;const{positionBufferData:t,offsetBufferData:e,intensityBufferData:i}=this._initBuffers();for(let i=0;it._pointToContainerPoint(e))),!n.intersects(i))return void this.completeRender();a=i.intersection(n)}this._heater||(this._heater=r(this.canvas)),this._heater.radius(e.options.radius||this._heater.defaultRadius,e.options.blur),e.options.gradient&&this._heater.gradient(e.options.gradient),this._heater.max(e.options.max),this._heatViews||(this._heatViews=[]);const s=e.getData();if(0===s.length)return void this.completeRender();if(this._heater){const t=this._heater._width,e=this._heater._height;if(Math.min(t,e)<=0)return void this.completeRender()}const o=this._heatData(s,a);this._heater.data(o).draw(e.options.minOpacity),this.completeRender()}drawOnInteracting(){this.draw()}_heatData(t,e){const i=this.getMap(),n=this.layer,s=i.getProjection(),r=[],o=this._heater._r,h=void 0===n.options.max?1:n.options.max,f=o/2,d=[],l=i.offsetPlatform(),u=Math.abs(l.x)%f,c=Math.abs(l.y)%f;let p,g,_,m,v,y;const{xmin:x,ymin:w,xmax:b,ymax:D}=e.expand(o);this._heatRadius=o;const I=new a.Coordinate(0,0);for(let e=0,a=t.length;eb||g.yD||(m=Math.floor((g.x-u)/f)+2,v=Math.floor((g.y-c)/f)+2,y=(void 0!==p[2]?+p[2]:.1)*n.options.heatValueScale,d[v]=d[v]||[],_=d[v][m],_?(_[0]=(_[0]*_[2]+g.x*y)/(_[2]+y),_[1]=(_[1]*_[2]+g.y*y)/(_[2]+y),_[2]+=y):d[v][m]=[g.x,g.y,y]);for(let t=0,e=d.length;tthis.canvas?this.canvas.width:1,height:()=>this.canvas?this.canvas.height:1},e={blend:{enable:!0,func:{dst:1,src:1}},depth:{enable:!1},viewport:t};this._pointShader=new i.reshader.MeshShader({name:"heatmap-point",vert:"attribute vec2 aPosition;\nattribute vec2 aOffset;\nattribute float aIntensity;\nvarying vec2 off, dim;\nvarying float vIntensity;\nuniform mat4 projViewModelMatrix;\nuniform float zoomScale;\nvoid main() {\n off = aOffset;\n dim = abs(off);\n vec2 pos = aPosition.xy + zoomScale * off;\n vIntensity = aIntensity / 255.0;\n gl_Position = projViewModelMatrix * vec4(pos, 0.0, 1.0);\n}",frag:"precision highp int;\nprecision highp float;\nvarying vec2 off, dim;\nvarying float vIntensity;\nvoid main() {\n float falloff = (1.0 - smoothstep(0.0, 1.0, length(off / dim)));\n float intensity = falloff * vIntensity;\n gl_FragColor = vec4(intensity);\n}",wgslVert:"struct VertexInput {\n @location($i) aPosition: vec2f,\n @location($i) aOffset: vec2f,\n @location($i) aIntensity: f32,\n};\nstruct VertexOutput {\n @builtin(position) Position: vec4f,\n @location($o) off: vec2f,\n @location($o) dim: vec2f,\n @location($o) vIntensity: f32,\n};\nstruct GlobalUniforms {\n zoomScale: f32,\n};\nstruct MyAppUniforms {\n projViewModelMatrix: mat4x4f,\n};\n@group(0) @binding($b) var globalUniforms: GlobalUniforms;\n@group(0) @binding($b) var uniforms: MyAppUniforms;\n@vertex\nfn main(vertexInput: VertexInput) -> VertexOutput {\n var vertexOutput: VertexOutput;\n var off = vec2f(vertexInput.aOffset);\n vertexOutput.off = off;\n vertexOutput.dim = abs(vec2f(vertexInput.aOffset));\n let pos = vertexInput.aPosition.xy + globalUniforms.zoomScale * off;\n vertexOutput.vIntensity = f32(vertexInput.aIntensity) / 255.0;\n vertexOutput.Position = uniforms.projViewModelMatrix * vec4f(pos, 0.0, 1.0);\n return vertexOutput;\n}",wgslFrag:"struct FragmentInput {\n @location($i) off: vec2f,\n @location($i) dim: vec2f,\n @location($i) vIntensity: f32,\n};\n@fragment\nfn main(fragmentInput: FragmentInput) -> @location(0) vec4f {\n let falloff = 1.0 - smoothstep(0.0, 1.0, length(fragmentInput.off / fragmentInput.dim));\n let intensity = falloff * fragmentInput.vIntensity;\n return vec4f(intensity, intensity, intensity, intensity);\n}",extraCommandProps:e}),this._gradientShader=new i.reshader.MeshShader({name:"heatmap-gradient",vert:"attribute vec4 aPosition;\nvarying vec2 texcoord;\nvoid main() {\n texcoord = aPosition.xy * 0.5 + 0.5;\n gl_Position = aPosition;\n}",frag:"precision highp int;\nprecision highp float;\nuniform sampler2D source;\nvarying vec2 texcoord;\nfloat linstep(float low, float high, float value) {\n return clamp((value-low)/(high-low), 0.0, 1.0);\n}\nfloat fade(float low, float high, float value) {\n float mid = (low+high)*0.5;\n float range = (high-low)*0.5;\n float x = 1.0 - clamp(abs(mid-value)/range, 0.0, 1.0);\n return smoothstep(0.0, 1.0, x);\n}\nvec3 getColor(float intensity) {\n vec3 blue = vec3(0.0, 0.0, 1.0);\n vec3 cyan = vec3(0.0, 1.0, 1.0);\n vec3 green = vec3(0.0, 1.0, 0.0);\n vec3 yellow = vec3(1.0, 1.0, 0.0);\n vec3 red = vec3(1.0, 0.0, 0.0);\n vec3 color = (\n fade(-0.25, 0.25, intensity)*blue +\n fade(0.0, 0.5, intensity)*cyan +\n fade(0.25, 0.75, intensity)*green +\n fade(0.5, 1.0, intensity)*yellow +\n smoothstep(0.75, 1.0, intensity)*red\n );\n return color;\n}\nvec4 alphaFun(vec3 color, float intensity) {\n float alpha = smoothstep(0.00000000, 1.00000000, intensity);\n return vec4(color*alpha, alpha);\n}\nvoid main() {\n vec4 value = texture2D(source, texcoord);\n float intensity = smoothstep(0.0, 1.0, value.r);\n vec3 color = getColor(intensity);\n gl_FragColor = alphaFun(color, intensity);\n}",wgslVert:"struct VertexInput {\n @location($i) aPosition: vec4i,\n};\nstruct VertexOutput {\n @builtin(position) Position: vec4f,\n @location($o) texcoord: vec2f,\n};\n@vertex\nfn main(vertexInput: VertexInput) -> VertexOutput {\n var vertexOutput: VertexOutput;\n var position = vec4f(vertexInput.aPosition);\n vertexOutput.texcoord = vec2f(position.x * 0.5 + 0.5, -position.y * 0.5 + 0.5);\n vertexOutput.Position = position;\n return vertexOutput;\n}",wgslFrag:"struct FragmentInput {\n @location($i) texcoord: vec2f,\n};\n@group(0) @binding($b) var source: texture_2d;\n@group(0) @binding($b) var sourceSampler: sampler;\nfn linstep(low: f32, high: f32, value: f32) -> f32 {\n return clamp((value - low) / (high - low), 0.0, 1.0);\n}\nfn fade(low: f32, high: f32, value: f32) -> f32 {\n let mid = (low + high) * 0.5;\n let range = (high - low) * 0.5;\n let x = 1.0 - clamp(abs(mid - value) / range, 0.0, 1.0);\n return smoothstep(0.0, 1.0, x);\n}\nfn getColor(intensity: f32) -> vec3f {\n let blue = vec3f(0.0, 0.0, 1.0);\n let cyan = vec3f(0.0, 1.0, 1.0);\n let green = vec3f(0.0, 1.0, 0.0);\n let yellow = vec3f(1.0, 1.0, 0.0);\n let red = vec3f(1.0, 0.0, 0.0);\n let color = (\n fade(-0.25, 0.25, intensity) * blue +\n fade(0.0, 0.5, intensity) * cyan +\n fade(0.25, 0.75, intensity) * green +\n fade(0.5, 1.0, intensity) * yellow +\n smoothstep(0.75, 1.0, intensity) * red\n );\n return color;\n}\nfn alphaFun(color: vec3f, intensity: f32) -> vec4f {\n let alpha = smoothstep(0.00000000, 1.00000000, intensity);\n return vec4f(color * alpha, alpha);\n}\n@fragment\nfn main(fragmentInput: FragmentInput) -> @location(0) vec4f {\n let value = textureSample(source, sourceSampler, fragmentInput.texcoord);\n let intensity = smoothstep(0.0, 1.0, value.r);\n let color = getColor(intensity);\n let fragColor = alphaFun(color, intensity);\n return fragColor;\n}",extraCommandProps:{blend:{enable:!0,func:{src:1,dst:"one minus src alpha"}},depth:{enable:!1},viewport:t}})}_initData(){this.bufferIndex=0,this.offsetIndex=0,this.intensityIndex=0,this.pointCount=0,this.maxPointCount=10240;const{positionBufferData:t,offsetBufferData:e,intensityBufferData:i}=this._initBuffers();this.positionBufferData=t,this.offsetBufferData=e,this.intensityBufferData=i}_initBuffers(){const t=this.getMap().getRenderer().isWebGPU();return{positionBufferData:new Float32Array(2*this.maxPointCount*6),offsetBufferData:new Float32Array(2*this.maxPointCount*6),intensityBufferData:t?new Float32Array(1*this.maxPointCount*6):new Uint8Array(1*this.maxPointCount*6)}}_initMesh(){this._renderer=new i.reshader.Renderer(this.device),this._geometry=new i.reshader.Geometry({aPosition:this.positionBufferData,aOffset:this.offsetBufferData,aIntensity:this.intensityBufferData},null,6*this.pointCount,{positionSize:2}),this._geometry.generateBuffers(this.device),this._mesh=new i.reshader.Mesh(this._geometry),this._scene=new i.reshader.Scene([this._mesh]);const t=this.canvas,e=this.device.texture({type:"float16",min:"nearest",mag:"nearest",width:t.width,height:t.height});this._fbo=this.device.framebuffer({width:t.width,height:t.height,colors:[e],colorFormat:"rgba",depthStencil:!1});const n=new Int8Array([-1,-1,0,1,1,-1,0,1,-1,1,0,1,-1,1,0,1,1,-1,0,1,1,1,0,1]);this._gradientGeometry=new i.reshader.Geometry({aPosition:n},null,0,{positionSize:4}),this._gradientGeometry.generateBuffers(this.device),this._gradientMesh=new i.reshader.Mesh(this._gradientGeometry),this._gradientScene=new i.reshader.Scene([this._gradientMesh])}_initGradient(){const t=function(t){const e=document.createElement("canvas"),i=e.getContext("2d",{willReadFrequently:!0}),n=i.createLinearGradient(0,0,0,256);e.width=256,e.height=1;for(const e in t)n.addColorStop(+e,t[e]);return i.fillStyle=n,i.fillRect(0,0,1,256),{data:i.getImageData(0,0,1,256).data,width:e.width,height:e.height}}(this.layer.options.gradient);this._gradientTexture?this._gradientTexture.update?this._gradientTexture.update(t):this._gradientTexture(t):this._gradientTexture=this.device.texture(t),this._gradientMesh.setUniform("source",this._fbo)}addVertex(t,e,i,n,a){const s=this.getMap(),r=s.getGLRes();o.set(t,e);const f=s.coordToPointAtRes(o,r,h);t=f.x,e=f.y,this.positionBufferData[this.bufferIndex++]=t,this.positionBufferData[this.bufferIndex++]=e,this.offsetBufferData[this.offsetIndex++]=i,this.offsetBufferData[this.offsetIndex++]=n,this.intensityBufferData[this.intensityIndex++]=255*a}addPoint(t,e,i){const n=this.layer.options.radius||25;null==i&&(i=.2);i/=this.layer.options.max||1,this._check();const a=n;return this.addVertex(t,e,-a,-a,i),this.addVertex(t,e,+a,-a,i),this.addVertex(t,e,-a,+a,i),this.addVertex(t,e,-a,+a,i),this.addVertex(t,e,+a,-a,i),this.addVertex(t,e,+a,+a,i),this.pointCount+=1}_check(){if(this.pointCount>=this.maxPointCount-1){this.maxPointCount+=10240;const{positionBufferData:t,offsetBufferData:e,intensityBufferData:i}=this._initBuffers();for(let i=0;i map._pointToContainerPoint(c)); 185 | //out of layer mask 186 | if (!maskExtent.intersects(extent)) { 187 | this.completeRender(); 188 | return; 189 | } 190 | displayExtent = extent.intersection(maskExtent); 191 | } 192 | 193 | if (!this._heater) { 194 | this._heater = simpleheat(this.canvas); 195 | } 196 | this._heater.radius(layer.options['radius'] || this._heater.defaultRadius, layer.options['blur']); 197 | if (layer.options['gradient']) { 198 | this._heater.gradient(layer.options['gradient']); 199 | } 200 | this._heater.max(layer.options['max']); 201 | //a cache of heat points' viewpoints. 202 | if (!this._heatViews) { 203 | this._heatViews = []; 204 | } 205 | 206 | const heats = layer.getData(); 207 | if (heats.length === 0) { 208 | this.completeRender(); 209 | return; 210 | } 211 | //fix https://github.com/maptalks/maptalks.heatmap/issues/55 212 | if (this._heater) { 213 | const width = this._heater._width, height = this._heater._height; 214 | if (Math.min(width, height) <= 0) { 215 | this.completeRender(); 216 | return; 217 | } 218 | } 219 | const data = this._heatData(heats, displayExtent); 220 | this._heater.data(data).draw(layer.options['minOpacity']); 221 | this.completeRender(); 222 | } 223 | 224 | drawOnInteracting() { 225 | this.draw(); 226 | } 227 | 228 | _heatData(heats, displayExtent) { 229 | const map = this.getMap(), 230 | layer = this.layer; 231 | const projection = map.getProjection(); 232 | const data = [], 233 | r = this._heater._r, 234 | max = layer.options['max'] === undefined ? 1 : layer.options['max'], 235 | cellSize = r / 2, 236 | grid = [], 237 | panePos = map.offsetPlatform(), 238 | offsetX = Math.abs(panePos.x) % cellSize, 239 | offsetY = Math.abs(panePos.y) % cellSize; 240 | let heat, p, cell, x, y, k; 241 | // displayExtent = displayExtent.expand(r).convertTo(c => new maptalks.Point(map._containerPointToPrj(c))); 242 | const { xmin, ymin, xmax, ymax } = displayExtent.expand(r); 243 | this._heatRadius = r; 244 | const coord = new maptalks.Coordinate(0, 0); 245 | for (let i = 0, l = heats.length; i < l; i++) { 246 | heat = heats[i]; 247 | if (!this._heatViews[i]) { 248 | this._heatViews[i] = projection.project(coord.set(heat[0], heat[1])); 249 | } 250 | p = this._heatViews[i]; 251 | //fix https://github.com/maptalks/maptalks.heatmap/issues/54 252 | // if (displayExtent.contains(p)) { 253 | p = map._prjToContainerPoint(p); 254 | if (p.x < xmin || p.x > xmax || p.y < ymin || p.y > ymax) { 255 | continue; 256 | } 257 | x = Math.floor((p.x - offsetX) / cellSize) + 2; 258 | y = Math.floor((p.y - offsetY) / cellSize) + 2; 259 | 260 | k = (heat[2] !== undefined ? +heat[2] : 0.1) * layer.options['heatValueScale']; 261 | 262 | grid[y] = grid[y] || []; 263 | cell = grid[y][x]; 264 | 265 | if (!cell) { 266 | grid[y][x] = [p.x, p.y, k]; 267 | 268 | } else { 269 | cell[0] = (cell[0] * cell[2] + (p.x) * k) / (cell[2] + k); // x 270 | cell[1] = (cell[1] * cell[2] + (p.y) * k) / (cell[2] + k); // y 271 | cell[2] += k; // cumulated intensity value 272 | } 273 | // } 274 | } 275 | for (let i = 0, l = grid.length; i < l; i++) { 276 | if (grid[i] && grid[i].length) { 277 | for (let j = 0, ll = grid[i].length; j < ll; j++) { 278 | cell = grid[i][j]; 279 | if (cell) { 280 | data.push([ 281 | Math.round(cell[0]), 282 | Math.round(cell[1]), 283 | Math.min(cell[2], max) 284 | ]); 285 | } 286 | } 287 | } 288 | } 289 | return data; 290 | } 291 | 292 | onZoomEnd() { 293 | delete this._heatViews; 294 | super.onZoomEnd.apply(this, arguments); 295 | } 296 | 297 | onResize() { 298 | super.onResize.apply(this, arguments); 299 | if (this.canvas) { 300 | this._heater._width = this.canvas.width; 301 | this._heater._height = this.canvas.height; 302 | } 303 | } 304 | 305 | onRemove() { 306 | this.clearHeatCache(); 307 | delete this._heater; 308 | } 309 | 310 | updateData() { 311 | } 312 | 313 | resetData() { 314 | this.clearHeatCache(); 315 | } 316 | 317 | updateGradient() { 318 | } 319 | 320 | clearHeatCache() { 321 | delete this._heatViews; 322 | } 323 | }); 324 | 325 | if (typeof CanvasCompatible !== 'undefined') { 326 | const HeatLayerGLRenderer = class extends CanvasCompatible(maptalks.renderer.LayerAbstractRenderer) { 327 | drawOnInteracting(event, timestamp, parentContext) { 328 | this.draw(timestamp, parentContext); 329 | } 330 | 331 | draw(timestamp, parentContext) { 332 | this.prepareCanvas(); 333 | if (!this._renderer) { 334 | return; 335 | } 336 | const heats = this.layer.getData(); 337 | if (heats.length !== this.pointCount) { 338 | for (let i = this.pointCount; i < heats.length; i++) { 339 | this.addPoint(...heats[i]); 340 | } 341 | this._updateGeometryData(); 342 | } 343 | const fbo = parentContext && parentContext.renderTarget && context.renderTarget.fbo; 344 | this._clearFBO(); 345 | this._geometry.setDrawCount(this.pointCount * 6); 346 | const map = this.getMap(); 347 | const glRes = map.getGLRes(); 348 | const uniforms = { 349 | zoomScale: map.getResolution() / glRes, 350 | projViewModelMatrix: map.projViewMatrix 351 | }; 352 | this._renderer.render(this._pointShader, uniforms, this._scene, this._fbo); 353 | this._renderer.render(this._gradientShader, null, this._gradientScene, fbo); 354 | } 355 | 356 | updateData(point) { 357 | this.addPoint(point[0], point[1], point[2]); 358 | } 359 | 360 | _clearFBO() { 361 | this.device.clear({ 362 | color: [0, 0, 0, 0], 363 | depth: 1, 364 | framebuffer: this._fbo 365 | }); 366 | } 367 | 368 | clearHeatCache() { 369 | 370 | } 371 | 372 | resetData() { 373 | this._reset(); 374 | } 375 | 376 | updateGradient() { 377 | this._initGradient(); 378 | } 379 | 380 | clear() { 381 | this._reset(); 382 | super.clear(); 383 | } 384 | 385 | _reset() { 386 | this.pointCount = 0; 387 | this.bufferIndex = 0; 388 | this.offsetIndex = 0; 389 | this.intensityIndex = 0; 390 | } 391 | 392 | initContext() { 393 | this._initData(); 394 | this._initMesh(); 395 | this._initGradient(); 396 | const viewport = { 397 | x : 0, 398 | y : 0, 399 | width : () => { 400 | return this.canvas ? this.canvas.width : 1; 401 | }, 402 | height : () => { 403 | return this.canvas ? this.canvas.height : 1; 404 | } 405 | }; 406 | const extraCommandProps = { 407 | blend: { 408 | enable: true, 409 | func: { 410 | dst: 1, 411 | src: 1 412 | } 413 | }, 414 | depth: { 415 | enable: false, 416 | }, 417 | viewport 418 | } 419 | this._pointShader = new reshader.MeshShader({ 420 | name: 'heatmap-point', 421 | vert, 422 | frag, 423 | wgslVert, 424 | wgslFrag, 425 | extraCommandProps 426 | }); 427 | 428 | this._gradientShader = new reshader.MeshShader({ 429 | name: 'heatmap-gradient', 430 | vert: gradientVert, 431 | frag: gradientFrag, 432 | wgslVert: wgslGradientVert, 433 | wgslFrag: wgslGradientFrag, 434 | extraCommandProps: { 435 | blend: { 436 | enable: true, 437 | func: { 438 | src: 1, 439 | dst: 'one minus src alpha' 440 | } 441 | }, 442 | depth: { 443 | enable: false, 444 | }, 445 | viewport 446 | } 447 | }); 448 | } 449 | 450 | _initData() { 451 | this.bufferIndex = 0; 452 | this.offsetIndex = 0; 453 | this.intensityIndex = 0; 454 | this.pointCount = 0; 455 | this.maxPointCount = 1024 * 10; 456 | 457 | const { positionBufferData, offsetBufferData, intensityBufferData } = this._initBuffers(); 458 | this.positionBufferData = positionBufferData; 459 | this.offsetBufferData = offsetBufferData; 460 | this.intensityBufferData = intensityBufferData; 461 | } 462 | 463 | _initBuffers() { 464 | const map = this.getMap(); 465 | const isWebGPU = map.getRenderer().isWebGPU(); 466 | const vertexSize = 2; 467 | const offsetSize = 2; 468 | const intensitySize = 1; 469 | const positionBufferData = new Float32Array( 470 | this.maxPointCount * vertexSize * 6 471 | ); 472 | const offsetBufferData = new Float32Array( 473 | this.maxPointCount * offsetSize * 6 474 | ); 475 | const intensityBufferData = isWebGPU ? new Float32Array( 476 | this.maxPointCount * intensitySize * 6 477 | ) : new Uint8Array( 478 | this.maxPointCount * intensitySize * 6 479 | ); 480 | return { positionBufferData, offsetBufferData, intensityBufferData }; 481 | } 482 | 483 | _initMesh() { 484 | this._renderer = new reshader.Renderer(this.device); 485 | this._geometry = new reshader.Geometry( 486 | { 487 | aPosition: this.positionBufferData, 488 | aOffset: this.offsetBufferData, 489 | aIntensity: this.intensityBufferData 490 | }, 491 | null, 492 | this.pointCount * 6, 493 | { 494 | positionSize: 2 495 | } 496 | ); 497 | this._geometry.generateBuffers(this.device); 498 | this._mesh = new reshader.Mesh(this._geometry); 499 | this._scene = new reshader.Scene([this._mesh]); 500 | const canvas = this.canvas; 501 | const color = this.device.texture({ 502 | type: 'float16', 503 | min: 'nearest', 504 | mag: 'nearest', 505 | width: canvas.width, 506 | height: canvas.height 507 | }); 508 | this._fbo = this.device.framebuffer({ 509 | width: canvas.width, 510 | height: canvas.height, 511 | colors: [color], 512 | colorFormat: 'rgba', 513 | depthStencil: false 514 | }); 515 | 516 | const quad = new Int8Array([ 517 | -1, -1, 0, 1, 1, -1, 0, 1, -1, 1, 0, 1, -1, 1, 0, 1, 1, -1, 0, 1, 1, 1, 0, 1 518 | ]); 519 | this._gradientGeometry = new reshader.Geometry( 520 | { 521 | aPosition: quad 522 | }, 523 | null, 524 | 0, 525 | { 526 | positionSize: 4 527 | } 528 | ); 529 | this._gradientGeometry.generateBuffers(this.device); 530 | this._gradientMesh = new reshader.Mesh(this._gradientGeometry); 531 | this._gradientScene = new reshader.Scene([this._gradientMesh]); 532 | } 533 | 534 | _initGradient() { 535 | const gradientData = gradient(this.layer.options['gradient']); 536 | if (this._gradientTexture) { 537 | if (this._gradientTexture.update) { 538 | this._gradientTexture.update(gradientData); 539 | } else { 540 | this._gradientTexture(gradientData); 541 | } 542 | } else { 543 | this._gradientTexture = this.device.texture(gradientData); 544 | } 545 | this._gradientMesh.setUniform('source', this._fbo); 546 | } 547 | 548 | addVertex(x, y, xs, ys, intensity) { 549 | const map = this.getMap(); 550 | const glRes = map.getGLRes(); 551 | COORD.set(x, y); 552 | const point = map.coordToPointAtRes(COORD, glRes, POINT); 553 | x = point.x; 554 | y = point.y; 555 | this.positionBufferData[this.bufferIndex++] = x; 556 | this.positionBufferData[this.bufferIndex++] = y; 557 | this.offsetBufferData[this.offsetIndex++] = xs; 558 | this.offsetBufferData[this.offsetIndex++] = ys; 559 | this.intensityBufferData[this.intensityIndex++] = intensity * 255; 560 | } 561 | 562 | addPoint(x, y, intensity) { 563 | const size = this.layer.options['radius'] || 25; 564 | if (intensity == null) { 565 | intensity = 0.2; 566 | } 567 | const max = this.layer.options['max'] || 1; 568 | intensity /= max; 569 | this._check(); 570 | const s = size; 571 | this.addVertex(x, y, -s, -s, intensity); 572 | this.addVertex(x, y, +s, -s, intensity); 573 | this.addVertex(x, y, -s, +s, intensity); 574 | this.addVertex(x, y, -s, +s, intensity); 575 | this.addVertex(x, y, +s, -s, intensity); 576 | this.addVertex(x, y, +s, +s, intensity); 577 | return (this.pointCount += 1); 578 | } 579 | 580 | _check() { 581 | if (this.pointCount >= this.maxPointCount - 1) { 582 | this.maxPointCount += 1024 * 10; 583 | const { positionBufferData, offsetBufferData, intensityBufferData } = this._initBuffers(); 584 | for (let i = 0; i < this.bufferIndex; i++) { 585 | positionBufferData[i] = this.positionBufferData[i]; 586 | offsetBufferData[i] = this.offsetBufferData[i]; 587 | } 588 | for (let i = 0; i < this.intensityIndex; i++) { 589 | intensityBufferData[i] = this.intensityBufferData[i]; 590 | } 591 | this.positionBufferData = positionBufferData; 592 | this.offsetBufferData = offsetBufferData; 593 | this.intensityBufferData = intensityBufferData; 594 | this._updateGeometryData(); 595 | } 596 | } 597 | 598 | _updateGeometryData() { 599 | this._geometry.updateData('aPosition', this.positionBufferData); 600 | this._geometry.updateData('aOffset', this.offsetBufferData); 601 | this._geometry.updateData('aIntensity', this.intensityBufferData); 602 | } 603 | 604 | onResize(params) { 605 | if (this._fbo && this.canvas) { 606 | const canvas = this.canvas; 607 | if (this._fbo.width !== canvas.width || this._fbo.height !== canvas.height) { 608 | this._fbo.resize(canvas.width, canvas.height); 609 | } 610 | } 611 | super.onResize(params); 612 | } 613 | 614 | onRemove() { 615 | this._reset(); 616 | if (this._pointShader) { 617 | this._pointShader.dispose(); 618 | delete this._pointShader; 619 | } 620 | if (this._gradientShader) { 621 | this._gradientShader.dispose(); 622 | delete this._gradientShader; 623 | } 624 | if (this._gradientTexture) { 625 | this._gradientTexture.destroy(); 626 | delete this._gradientTexture; 627 | } 628 | if (this._mesh) { 629 | this._mesh.dispose(); 630 | delete this._mesh; 631 | } 632 | if (this._geometry) { 633 | this._geometry.dispose(); 634 | delete this._geometry; 635 | } 636 | if (this._fbo) { 637 | this._fbo.destroy(); 638 | delete this._fbo; 639 | } 640 | if (this._gradientMesh) { 641 | this._gradientMesh.dispose(); 642 | delete this._gradientMesh; 643 | } 644 | if (this._gradientGeometry) { 645 | this._gradientGeometry.dispose(); 646 | delete this._gradientGeometry; 647 | } 648 | delete this.positionBufferData; 649 | delete this.offsetBufferData; 650 | delete this.intensityBufferData; 651 | delete this._renderer; 652 | delete this._scene; 653 | delete this._gradientScene; 654 | super.onRemove(); 655 | } 656 | }; 657 | HeatLayer.registerRenderer('gl', HeatLayerGLRenderer); 658 | HeatLayer.registerRenderer('gpu', HeatLayerGLRenderer); 659 | } 660 | 661 | function gradient(grad) { 662 | // create a 256x1 gradient that we'll use to turn a grayscale heatmap into a colored one 663 | const canvas = document.createElement('canvas'), 664 | ctx = canvas.getContext('2d', {willReadFrequently: true}), 665 | gradient = ctx.createLinearGradient(0, 0, 0, 256); 666 | 667 | canvas.width = 256; 668 | canvas.height = 1; 669 | 670 | for (const i in grad) { 671 | gradient.addColorStop(+i, grad[i]); 672 | } 673 | 674 | ctx.fillStyle = gradient; 675 | ctx.fillRect(0, 0, 1, 256); 676 | 677 | return { 678 | data: ctx.getImageData(0, 0, 1, 256).data, 679 | width: canvas.width, 680 | height: canvas.height 681 | }; 682 | } 683 | -------------------------------------------------------------------------------- /dist/maptalks.heatmap.es.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"maptalks.heatmap.es.js","sources":["../node_modules/simpleheat/simpleheat.js","../index.js"],"sourcesContent":["'use strict';\n\nif (typeof module !== 'undefined') module.exports = simpleheat;\n\nfunction simpleheat(canvas) {\n if (!(this instanceof simpleheat)) return new simpleheat(canvas);\n\n this._canvas = canvas = typeof canvas === 'string' ? document.getElementById(canvas) : canvas;\n\n this._ctx = canvas.getContext('2d');\n this._width = canvas.width;\n this._height = canvas.height;\n\n this._max = 1;\n this._data = [];\n}\n\nsimpleheat.prototype = {\n\n defaultRadius: 25,\n\n defaultGradient: {\n 0.4: 'blue',\n 0.6: 'cyan',\n 0.7: 'lime',\n 0.8: 'yellow',\n 1.0: 'red'\n },\n\n data: function (data) {\n this._data = data;\n return this;\n },\n\n max: function (max) {\n this._max = max;\n return this;\n },\n\n add: function (point) {\n this._data.push(point);\n return this;\n },\n\n clear: function () {\n this._data = [];\n return this;\n },\n\n radius: function (r, blur) {\n blur = blur === undefined ? 15 : blur;\n\n // create a grayscale blurred circle image that we'll use for drawing points\n var circle = this._circle = this._createCanvas(),\n ctx = circle.getContext('2d'),\n r2 = this._r = r + blur;\n\n circle.width = circle.height = r2 * 2;\n\n ctx.shadowOffsetX = ctx.shadowOffsetY = r2 * 2;\n ctx.shadowBlur = blur;\n ctx.shadowColor = 'black';\n\n ctx.beginPath();\n ctx.arc(-r2, -r2, r, 0, Math.PI * 2, true);\n ctx.closePath();\n ctx.fill();\n\n return this;\n },\n\n resize: function () {\n this._width = this._canvas.width;\n this._height = this._canvas.height;\n },\n\n gradient: function (grad) {\n // create a 256x1 gradient that we'll use to turn a grayscale heatmap into a colored one\n var canvas = this._createCanvas(),\n ctx = canvas.getContext('2d'),\n gradient = ctx.createLinearGradient(0, 0, 0, 256);\n\n canvas.width = 1;\n canvas.height = 256;\n\n for (var i in grad) {\n gradient.addColorStop(+i, grad[i]);\n }\n\n ctx.fillStyle = gradient;\n ctx.fillRect(0, 0, 1, 256);\n\n this._grad = ctx.getImageData(0, 0, 1, 256).data;\n\n return this;\n },\n\n draw: function (minOpacity) {\n if (!this._circle) this.radius(this.defaultRadius);\n if (!this._grad) this.gradient(this.defaultGradient);\n\n var ctx = this._ctx;\n\n ctx.clearRect(0, 0, this._width, this._height);\n\n // draw a grayscale heatmap by putting a blurred circle at each data point\n for (var i = 0, len = this._data.length, p; i < len; i++) {\n p = this._data[i];\n ctx.globalAlpha = Math.max(p[2] / this._max, minOpacity === undefined ? 0.05 : minOpacity);\n ctx.drawImage(this._circle, p[0] - this._r, p[1] - this._r);\n }\n\n // colorize the heatmap, using opacity value of each pixel to get the right color from our gradient\n var colored = ctx.getImageData(0, 0, this._width, this._height);\n this._colorize(colored.data, this._grad);\n ctx.putImageData(colored, 0, 0);\n\n return this;\n },\n\n _colorize: function (pixels, gradient) {\n for (var i = 0, len = pixels.length, j; i < len; i += 4) {\n j = pixels[i + 3] * 4; // get gradient color from opacity value\n\n if (j) {\n pixels[i] = gradient[j];\n pixels[i + 1] = gradient[j + 1];\n pixels[i + 2] = gradient[j + 2];\n }\n }\n },\n\n _createCanvas: function () {\n if (typeof document !== 'undefined') {\n return document.createElement('canvas');\n } else {\n // create a new canvas instance in node.js\n // the canvas class needs to have a default constructor without any parameter\n return new this._canvas.constructor();\n }\n }\n};\n","import * as maptalks from 'maptalks';\nimport simpleheat from 'simpleheat';\nimport { reshader, CanvasCompatible } from '@maptalks/gl';\nimport vert from './glsl/points.vert';\nimport frag from './glsl/points.frag';\nimport gradientVert from './glsl/gradient.vert';\nimport gradientFrag from './glsl/gradient.frag';\n\nimport wgslVert from './wgsl/points_vert.wgsl';\nimport wgslFrag from './wgsl/points_frag.wgsl';\nimport wgslGradientVert from './wgsl/gradient_vert.wgsl';\nimport wgslGradientFrag from './wgsl/gradient_frag.wgsl';\n\nconst options = {\n 'max': 1,\n 'gradient': {\n 0.4: 'blue',\n 0.6: 'cyan',\n 0.7: 'lime',\n 0.8: 'yellow',\n 1.0: 'red'\n },\n 'radius': 25,\n 'blur': 15,\n 'heatValueScale': 1,\n 'minOpacity': 0.05,\n 'hitDetect': false\n};\n\nconst COORD = new maptalks.Coordinate(0, 0);\nconst POINT = new maptalks.Point(0, 0);\n\nexport class HeatLayer extends maptalks.Layer {\n\n constructor(id, heats, options) {\n if (!Array.isArray(heats)) {\n options = heats;\n heats = null;\n }\n super(id, options);\n this._heats = heats || [];\n }\n\n getData() {\n return this._heats;\n }\n\n setData(heats) {\n this._heats = heats || [];\n return this._resetData();\n }\n\n addPoint(heat) {\n if (!heat) {\n return this;\n }\n if (heat[0] && Array.isArray(heat[0])) {\n maptalks.Util.pushIn(this._heats, heat);\n } else {\n this._heats.push(heat);\n }\n return this._update(heat);\n }\n\n onConfig(conf) {\n for (const p in conf) {\n if (p === 'gradient') {\n this._updateGradient();\n return this;\n }\n }\n return this;\n }\n\n _updateGradient() {\n const renderer = this._getRenderer();\n if (renderer) {\n renderer.updateGradient();\n renderer.setToRedraw();\n }\n return this;\n }\n\n _resetData() {\n const renderer = this._getRenderer();\n if (renderer) {\n renderer.resetData();\n renderer.setToRedraw();\n }\n return this;\n }\n\n _update(point) {\n const renderer = this._getRenderer();\n if (renderer) {\n renderer.updateData(point);\n renderer.setToRedraw();\n }\n }\n\n isEmpty() {\n if (!this._heats.length) {\n return true;\n }\n return false;\n }\n\n clear() {\n this._heats = [];\n this._resetData();\n this.fire('clear');\n return this;\n }\n\n /**\n * Export the HeatLayer's JSON.\n * @return {Object} layer's JSON\n */\n toJSON(options) {\n if (!options) {\n options = {};\n }\n const json = {\n 'type': this.getJSONType(),\n 'id': this.getId(),\n 'options': this.config()\n };\n const data = this.getData();\n if (options['clipExtent']) {\n let clipExtent = new maptalks.Extent(options['clipExtent']);\n const r = this._getHeatRadius();\n if (r) {\n clipExtent = clipExtent._expand(r);\n }\n const clipped = [];\n for (let i = 0, len = data.length; i < len; i++) {\n if (clipExtent.contains(new maptalks.Coordinate(data[i][0], data[i][1]))) {\n clipped.push(data[i]);\n }\n }\n json['data'] = clipped;\n } else {\n json['data'] = data;\n }\n\n return json;\n }\n\n /**\n * Reproduce a HeatLayer from layer's JSON.\n * @param {Object} json - layer's JSON\n * @return {maptalks.HeatLayer}\n * @static\n * @private\n * @function\n */\n static fromJSON(json) {\n if (!json || json['type'] !== 'HeatLayer') { return null; }\n return new HeatLayer(json['id'], json['data'], json['options']);\n }\n\n\n _getHeatRadius() {\n if (!this._getRenderer()) {\n return null;\n }\n return this._getRenderer()._heatRadius;\n }\n}\n\nHeatLayer.mergeOptions(options);\n\nHeatLayer.registerJSONType('HeatLayer');\n\nHeatLayer.registerRenderer('canvas', class extends maptalks.renderer.CanvasRenderer {\n\n draw() {\n const map = this.getMap(),\n layer = this.layer,\n extent = map.getContainerExtent();\n let maskExtent = this.prepareCanvas(),\n displayExtent = extent;\n if (maskExtent) {\n maskExtent = maskExtent.convertTo(c => map._pointToContainerPoint(c));\n //out of layer mask\n if (!maskExtent.intersects(extent)) {\n this.completeRender();\n return;\n }\n displayExtent = extent.intersection(maskExtent);\n }\n\n if (!this._heater) {\n this._heater = simpleheat(this.canvas);\n }\n this._heater.radius(layer.options['radius'] || this._heater.defaultRadius, layer.options['blur']);\n if (layer.options['gradient']) {\n this._heater.gradient(layer.options['gradient']);\n }\n this._heater.max(layer.options['max']);\n //a cache of heat points' viewpoints.\n if (!this._heatViews) {\n this._heatViews = [];\n }\n\n const heats = layer.getData();\n if (heats.length === 0) {\n this.completeRender();\n return;\n }\n //fix https://github.com/maptalks/maptalks.heatmap/issues/55\n if (this._heater) {\n const width = this._heater._width, height = this._heater._height;\n if (Math.min(width, height) <= 0) {\n this.completeRender();\n return;\n }\n }\n const data = this._heatData(heats, displayExtent);\n this._heater.data(data).draw(layer.options['minOpacity']);\n this.completeRender();\n }\n\n drawOnInteracting() {\n this.draw();\n }\n\n _heatData(heats, displayExtent) {\n const map = this.getMap(),\n layer = this.layer;\n const projection = map.getProjection();\n const data = [],\n r = this._heater._r,\n max = layer.options['max'] === undefined ? 1 : layer.options['max'],\n cellSize = r / 2,\n grid = [],\n panePos = map.offsetPlatform(),\n offsetX = Math.abs(panePos.x) % cellSize,\n offsetY = Math.abs(panePos.y) % cellSize;\n let heat, p, cell, x, y, k;\n // displayExtent = displayExtent.expand(r).convertTo(c => new maptalks.Point(map._containerPointToPrj(c)));\n const { xmin, ymin, xmax, ymax } = displayExtent.expand(r);\n this._heatRadius = r;\n const coord = new maptalks.Coordinate(0, 0);\n for (let i = 0, l = heats.length; i < l; i++) {\n heat = heats[i];\n if (!this._heatViews[i]) {\n this._heatViews[i] = projection.project(coord.set(heat[0], heat[1]));\n }\n p = this._heatViews[i];\n //fix https://github.com/maptalks/maptalks.heatmap/issues/54\n // if (displayExtent.contains(p)) {\n p = map._prjToContainerPoint(p);\n if (p.x < xmin || p.x > xmax || p.y < ymin || p.y > ymax) {\n continue;\n }\n x = Math.floor((p.x - offsetX) / cellSize) + 2;\n y = Math.floor((p.y - offsetY) / cellSize) + 2;\n\n k = (heat[2] !== undefined ? +heat[2] : 0.1) * layer.options['heatValueScale'];\n\n grid[y] = grid[y] || [];\n cell = grid[y][x];\n\n if (!cell) {\n grid[y][x] = [p.x, p.y, k];\n\n } else {\n cell[0] = (cell[0] * cell[2] + (p.x) * k) / (cell[2] + k); // x\n cell[1] = (cell[1] * cell[2] + (p.y) * k) / (cell[2] + k); // y\n cell[2] += k; // cumulated intensity value\n }\n // }\n }\n for (let i = 0, l = grid.length; i < l; i++) {\n if (grid[i] && grid[i].length) {\n for (let j = 0, ll = grid[i].length; j < ll; j++) {\n cell = grid[i][j];\n if (cell) {\n data.push([\n Math.round(cell[0]),\n Math.round(cell[1]),\n Math.min(cell[2], max)\n ]);\n }\n }\n }\n }\n return data;\n }\n\n onZoomEnd() {\n delete this._heatViews;\n super.onZoomEnd.apply(this, arguments);\n }\n\n onResize() {\n super.onResize.apply(this, arguments);\n if (this.canvas) {\n this._heater._width = this.canvas.width;\n this._heater._height = this.canvas.height;\n }\n }\n\n onRemove() {\n this.clearHeatCache();\n delete this._heater;\n }\n\n updateData() {\n }\n\n resetData() {\n this.clearHeatCache();\n }\n\n updateGradient() {\n }\n\n clearHeatCache() {\n delete this._heatViews;\n }\n});\n\nif (typeof CanvasCompatible !== 'undefined') {\n const HeatLayerGLRenderer = class extends CanvasCompatible(maptalks.renderer.LayerAbstractRenderer) {\n drawOnInteracting(event, timestamp, parentContext) {\n this.draw(timestamp, parentContext);\n }\n\n draw(timestamp, parentContext) {\n this.prepareCanvas();\n if (!this._renderer) {\n return;\n }\n const heats = this.layer.getData();\n if (heats.length !== this.pointCount) {\n for (let i = this.pointCount; i < heats.length; i++) {\n this.addPoint(...heats[i]);\n }\n this._updateGeometryData();\n }\n const fbo = parentContext && parentContext.renderTarget && context.renderTarget.fbo;\n this._clearFBO();\n this._geometry.setDrawCount(this.pointCount * 6);\n const map = this.getMap();\n const glRes = map.getGLRes();\n const uniforms = {\n zoomScale: map.getResolution() / glRes,\n projViewModelMatrix: map.projViewMatrix\n };\n this._renderer.render(this._pointShader, uniforms, this._scene, this._fbo);\n this._renderer.render(this._gradientShader, null, this._gradientScene, fbo);\n }\n\n updateData(point) {\n this.addPoint(point[0], point[1], point[2]);\n }\n\n _clearFBO() {\n this.device.clear({\n color: [0, 0, 0, 0],\n depth: 1,\n framebuffer: this._fbo\n });\n }\n\n clearHeatCache() {\n\n }\n\n resetData() {\n this._reset();\n }\n\n updateGradient() {\n this._initGradient();\n }\n\n clear() {\n this._reset();\n super.clear();\n }\n\n _reset() {\n this.pointCount = 0;\n this.bufferIndex = 0;\n this.offsetIndex = 0;\n this.intensityIndex = 0;\n }\n\n initContext() {\n this._initData();\n this._initMesh();\n this._initGradient();\n const viewport = {\n x : 0,\n y : 0,\n width : () => {\n return this.canvas ? this.canvas.width : 1;\n },\n height : () => {\n return this.canvas ? this.canvas.height : 1;\n }\n };\n const extraCommandProps = {\n blend: {\n enable: true,\n func: {\n dst: 1,\n src: 1\n }\n },\n depth: {\n enable: false,\n },\n viewport\n }\n this._pointShader = new reshader.MeshShader({\n name: 'heatmap-point',\n vert,\n frag,\n wgslVert,\n wgslFrag,\n extraCommandProps\n });\n\n this._gradientShader = new reshader.MeshShader({\n name: 'heatmap-gradient',\n vert: gradientVert,\n frag: gradientFrag,\n wgslVert: wgslGradientVert,\n wgslFrag: wgslGradientFrag,\n extraCommandProps: {\n blend: {\n enable: true,\n func: {\n src: 1,\n dst: 'one minus src alpha'\n }\n },\n depth: {\n enable: false,\n },\n viewport\n }\n });\n }\n\n _initData() {\n this.bufferIndex = 0;\n this.offsetIndex = 0;\n this.intensityIndex = 0;\n this.pointCount = 0;\n this.maxPointCount = 1024 * 10;\n\n const { positionBufferData, offsetBufferData, intensityBufferData } = this._initBuffers();\n this.positionBufferData = positionBufferData;\n this.offsetBufferData = offsetBufferData;\n this.intensityBufferData = intensityBufferData;\n }\n\n _initBuffers() {\n const map = this.getMap();\n const isWebGPU = map.getRenderer().isWebGPU();\n const vertexSize = 2;\n const offsetSize = 2;\n const intensitySize = 1;\n const positionBufferData = new Float32Array(\n this.maxPointCount * vertexSize * 6\n );\n const offsetBufferData = new Float32Array(\n this.maxPointCount * offsetSize * 6\n );\n const intensityBufferData = isWebGPU ? new Float32Array(\n this.maxPointCount * intensitySize * 6\n ) : new Uint8Array(\n this.maxPointCount * intensitySize * 6\n );\n return { positionBufferData, offsetBufferData, intensityBufferData };\n }\n\n _initMesh() {\n this._renderer = new reshader.Renderer(this.device);\n this._geometry = new reshader.Geometry(\n {\n aPosition: this.positionBufferData,\n aOffset: this.offsetBufferData,\n aIntensity: this.intensityBufferData\n },\n null,\n this.pointCount * 6,\n {\n positionSize: 2\n }\n );\n this._geometry.generateBuffers(this.device);\n this._mesh = new reshader.Mesh(this._geometry);\n this._scene = new reshader.Scene([this._mesh]);\n const canvas = this.canvas;\n const color = this.device.texture({\n type: 'float16',\n min: 'nearest',\n mag: 'nearest',\n width: canvas.width,\n height: canvas.height\n });\n this._fbo = this.device.framebuffer({\n width: canvas.width,\n height: canvas.height,\n colors: [color],\n colorFormat: 'rgba',\n depthStencil: false\n });\n\n const quad = new Int8Array([\n -1, -1, 0, 1, 1, -1, 0, 1, -1, 1, 0, 1, -1, 1, 0, 1, 1, -1, 0, 1, 1, 1, 0, 1\n ]);\n this._gradientGeometry = new reshader.Geometry(\n {\n aPosition: quad\n },\n null,\n 0,\n {\n positionSize: 4\n }\n );\n this._gradientGeometry.generateBuffers(this.device);\n this._gradientMesh = new reshader.Mesh(this._gradientGeometry);\n this._gradientScene = new reshader.Scene([this._gradientMesh]);\n }\n\n _initGradient() {\n const gradientData = gradient(this.layer.options['gradient']);\n if (this._gradientTexture) {\n if (this._gradientTexture.update) {\n this._gradientTexture.update(gradientData);\n } else {\n this._gradientTexture(gradientData);\n }\n } else {\n this._gradientTexture = this.device.texture(gradientData);\n }\n this._gradientMesh.setUniform('source', this._fbo);\n }\n\n addVertex(x, y, xs, ys, intensity) {\n const map = this.getMap();\n const glRes = map.getGLRes();\n COORD.set(x, y);\n const point = map.coordToPointAtRes(COORD, glRes, POINT);\n x = point.x;\n y = point.y;\n this.positionBufferData[this.bufferIndex++] = x;\n this.positionBufferData[this.bufferIndex++] = y;\n this.offsetBufferData[this.offsetIndex++] = xs;\n this.offsetBufferData[this.offsetIndex++] = ys;\n this.intensityBufferData[this.intensityIndex++] = intensity * 255;\n }\n\n addPoint(x, y, intensity) {\n const size = this.layer.options['radius'] || 25;\n if (intensity == null) {\n intensity = 0.2;\n }\n const max = this.layer.options['max'] || 1;\n intensity /= max;\n this._check();\n const s = size;\n this.addVertex(x, y, -s, -s, intensity);\n this.addVertex(x, y, +s, -s, intensity);\n this.addVertex(x, y, -s, +s, intensity);\n this.addVertex(x, y, -s, +s, intensity);\n this.addVertex(x, y, +s, -s, intensity);\n this.addVertex(x, y, +s, +s, intensity);\n return (this.pointCount += 1);\n }\n\n _check() {\n if (this.pointCount >= this.maxPointCount - 1) {\n this.maxPointCount += 1024 * 10;\n const { positionBufferData, offsetBufferData, intensityBufferData } = this._initBuffers();\n for (let i = 0; i < this.bufferIndex; i++) {\n positionBufferData[i] = this.positionBufferData[i];\n offsetBufferData[i] = this.offsetBufferData[i];\n }\n for (let i = 0; i < this.intensityIndex; i++) {\n intensityBufferData[i] = this.intensityBufferData[i];\n }\n this.positionBufferData = positionBufferData;\n this.offsetBufferData = offsetBufferData;\n this.intensityBufferData = intensityBufferData;\n this._updateGeometryData();\n }\n }\n\n _updateGeometryData() {\n this._geometry.updateData('aPosition', this.positionBufferData);\n this._geometry.updateData('aOffset', this.offsetBufferData);\n this._geometry.updateData('aIntensity', this.intensityBufferData);\n }\n\n onResize(params) {\n if (this._fbo && this.canvas) {\n const canvas = this.canvas;\n if (this._fbo.width !== canvas.width || this._fbo.height !== canvas.height) {\n this._fbo.resize(canvas.width, canvas.height);\n }\n }\n super.onResize(params);\n }\n\n onRemove() {\n this._reset();\n if (this._pointShader) {\n this._pointShader.dispose();\n delete this._pointShader;\n }\n if (this._gradientShader) {\n this._gradientShader.dispose();\n delete this._gradientShader;\n }\n if (this._gradientTexture) {\n this._gradientTexture.destroy();\n delete this._gradientTexture;\n }\n if (this._mesh) {\n this._mesh.dispose();\n delete this._mesh;\n }\n if (this._geometry) {\n this._geometry.dispose();\n delete this._geometry;\n }\n if (this._fbo) {\n this._fbo.destroy();\n delete this._fbo;\n }\n if (this._gradientMesh) {\n this._gradientMesh.dispose();\n delete this._gradientMesh;\n }\n if (this._gradientGeometry) {\n this._gradientGeometry.dispose();\n delete this._gradientGeometry;\n }\n delete this.positionBufferData;\n delete this.offsetBufferData;\n delete this.intensityBufferData;\n delete this._renderer;\n delete this._scene;\n delete this._gradientScene;\n super.onRemove();\n }\n };\n HeatLayer.registerRenderer('gl', HeatLayerGLRenderer);\n HeatLayer.registerRenderer('gpu', HeatLayerGLRenderer);\n}\n\nfunction gradient(grad) {\n // create a 256x1 gradient that we'll use to turn a grayscale heatmap into a colored one\n const canvas = document.createElement('canvas'),\n ctx = canvas.getContext('2d', {willReadFrequently: true}),\n gradient = ctx.createLinearGradient(0, 0, 0, 256);\n\n canvas.width = 256;\n canvas.height = 1;\n\n for (const i in grad) {\n gradient.addColorStop(+i, grad[i]);\n }\n\n ctx.fillStyle = gradient;\n ctx.fillRect(0, 0, 1, 256);\n\n return {\n data: ctx.getImageData(0, 0, 1, 256).data,\n width: canvas.width,\n height: canvas.height\n };\n}\n"],"names":["simpleheat","canvas","this","_canvas","document","getElementById","_ctx","getContext","_width","width","_height","height","_max","_data","exports","prototype","defaultRadius","defaultGradient","data","max","add","point","push","clear","radius","r","blur","undefined","circle","_circle","_createCanvas","ctx","r2","_r","shadowOffsetX","shadowOffsetY","shadowBlur","shadowColor","beginPath","arc","Math","PI","closePath","fill","resize","gradient","grad","createLinearGradient","i","addColorStop","fillStyle","fillRect","_grad","getImageData","draw","minOpacity","clearRect","p","len","length","globalAlpha","drawImage","colored","_colorize","putImageData","pixels","j","createElement","constructor","COORD","maptalks","Coordinate","POINT","Point","HeatLayer","Layer","id","heats","options","Array","isArray","super","_heats","getData","setData","_resetData","addPoint","heat","Util","pushIn","_update","onConfig","conf","_updateGradient","renderer","_getRenderer","updateGradient","setToRedraw","resetData","updateData","isEmpty","fire","toJSON","json","type","getJSONType","getId","config","clipExtent","Extent","_getHeatRadius","_expand","clipped","contains","static","_heatRadius","mergeOptions","heatValueScale","hitDetect","registerJSONType","registerRenderer","CanvasRenderer","map","getMap","layer","extent","getContainerExtent","maskExtent","prepareCanvas","displayExtent","convertTo","c","_pointToContainerPoint","intersects","completeRender","intersection","_heater","_heatViews","min","_heatData","drawOnInteracting","projection","getProjection","cellSize","grid","panePos","offsetPlatform","offsetX","abs","x","offsetY","y","cell","k","xmin","ymin","xmax","ymax","expand","coord","l","project","set","_prjToContainerPoint","floor","ll","round","onZoomEnd","apply","arguments","onResize","onRemove","clearHeatCache","CanvasCompatible","HeatLayerGLRenderer","LayerAbstractRenderer","event","timestamp","parentContext","_renderer","pointCount","_updateGeometryData","fbo","renderTarget","context","_clearFBO","_geometry","setDrawCount","glRes","getGLRes","uniforms","zoomScale","getResolution","projViewModelMatrix","projViewMatrix","render","_pointShader","_scene","_fbo","_gradientShader","_gradientScene","device","color","depth","framebuffer","_reset","_initGradient","bufferIndex","offsetIndex","intensityIndex","initContext","_initData","_initMesh","viewport","extraCommandProps","blend","enable","func","dst","src","reshader","MeshShader","name","vert","frag","wgslVert","wgslFrag","maxPointCount","positionBufferData","offsetBufferData","intensityBufferData","_initBuffers","isWebGPU","getRenderer","Float32Array","Uint8Array","Renderer","Geometry","aPosition","aOffset","aIntensity","positionSize","generateBuffers","_mesh","Mesh","Scene","texture","mag","colors","colorFormat","depthStencil","quad","Int8Array","_gradientGeometry","_gradientMesh","gradientData","willReadFrequently","_gradientTexture","update","setUniform","addVertex","xs","ys","intensity","coordToPointAtRes","size","_check","s","params","dispose","destroy"],"mappings":"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"} -------------------------------------------------------------------------------- /dist/maptalks.heatmap.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"maptalks.heatmap.js","sources":["../node_modules/simpleheat/simpleheat.js","../index.js"],"sourcesContent":["'use strict';\n\nif (typeof module !== 'undefined') module.exports = simpleheat;\n\nfunction simpleheat(canvas) {\n if (!(this instanceof simpleheat)) return new simpleheat(canvas);\n\n this._canvas = canvas = typeof canvas === 'string' ? document.getElementById(canvas) : canvas;\n\n this._ctx = canvas.getContext('2d');\n this._width = canvas.width;\n this._height = canvas.height;\n\n this._max = 1;\n this._data = [];\n}\n\nsimpleheat.prototype = {\n\n defaultRadius: 25,\n\n defaultGradient: {\n 0.4: 'blue',\n 0.6: 'cyan',\n 0.7: 'lime',\n 0.8: 'yellow',\n 1.0: 'red'\n },\n\n data: function (data) {\n this._data = data;\n return this;\n },\n\n max: function (max) {\n this._max = max;\n return this;\n },\n\n add: function (point) {\n this._data.push(point);\n return this;\n },\n\n clear: function () {\n this._data = [];\n return this;\n },\n\n radius: function (r, blur) {\n blur = blur === undefined ? 15 : blur;\n\n // create a grayscale blurred circle image that we'll use for drawing points\n var circle = this._circle = this._createCanvas(),\n ctx = circle.getContext('2d'),\n r2 = this._r = r + blur;\n\n circle.width = circle.height = r2 * 2;\n\n ctx.shadowOffsetX = ctx.shadowOffsetY = r2 * 2;\n ctx.shadowBlur = blur;\n ctx.shadowColor = 'black';\n\n ctx.beginPath();\n ctx.arc(-r2, -r2, r, 0, Math.PI * 2, true);\n ctx.closePath();\n ctx.fill();\n\n return this;\n },\n\n resize: function () {\n this._width = this._canvas.width;\n this._height = this._canvas.height;\n },\n\n gradient: function (grad) {\n // create a 256x1 gradient that we'll use to turn a grayscale heatmap into a colored one\n var canvas = this._createCanvas(),\n ctx = canvas.getContext('2d'),\n gradient = ctx.createLinearGradient(0, 0, 0, 256);\n\n canvas.width = 1;\n canvas.height = 256;\n\n for (var i in grad) {\n gradient.addColorStop(+i, grad[i]);\n }\n\n ctx.fillStyle = gradient;\n ctx.fillRect(0, 0, 1, 256);\n\n this._grad = ctx.getImageData(0, 0, 1, 256).data;\n\n return this;\n },\n\n draw: function (minOpacity) {\n if (!this._circle) this.radius(this.defaultRadius);\n if (!this._grad) this.gradient(this.defaultGradient);\n\n var ctx = this._ctx;\n\n ctx.clearRect(0, 0, this._width, this._height);\n\n // draw a grayscale heatmap by putting a blurred circle at each data point\n for (var i = 0, len = this._data.length, p; i < len; i++) {\n p = this._data[i];\n ctx.globalAlpha = Math.max(p[2] / this._max, minOpacity === undefined ? 0.05 : minOpacity);\n ctx.drawImage(this._circle, p[0] - this._r, p[1] - this._r);\n }\n\n // colorize the heatmap, using opacity value of each pixel to get the right color from our gradient\n var colored = ctx.getImageData(0, 0, this._width, this._height);\n this._colorize(colored.data, this._grad);\n ctx.putImageData(colored, 0, 0);\n\n return this;\n },\n\n _colorize: function (pixels, gradient) {\n for (var i = 0, len = pixels.length, j; i < len; i += 4) {\n j = pixels[i + 3] * 4; // get gradient color from opacity value\n\n if (j) {\n pixels[i] = gradient[j];\n pixels[i + 1] = gradient[j + 1];\n pixels[i + 2] = gradient[j + 2];\n }\n }\n },\n\n _createCanvas: function () {\n if (typeof document !== 'undefined') {\n return document.createElement('canvas');\n } else {\n // create a new canvas instance in node.js\n // the canvas class needs to have a default constructor without any parameter\n return new this._canvas.constructor();\n }\n }\n};\n","import * as maptalks from 'maptalks';\nimport simpleheat from 'simpleheat';\nimport { reshader, CanvasCompatible } from '@maptalks/gl';\nimport vert from './glsl/points.vert';\nimport frag from './glsl/points.frag';\nimport gradientVert from './glsl/gradient.vert';\nimport gradientFrag from './glsl/gradient.frag';\n\nimport wgslVert from './wgsl/points_vert.wgsl';\nimport wgslFrag from './wgsl/points_frag.wgsl';\nimport wgslGradientVert from './wgsl/gradient_vert.wgsl';\nimport wgslGradientFrag from './wgsl/gradient_frag.wgsl';\n\nconst options = {\n 'max': 1,\n 'gradient': {\n 0.4: 'blue',\n 0.6: 'cyan',\n 0.7: 'lime',\n 0.8: 'yellow',\n 1.0: 'red'\n },\n 'radius': 25,\n 'blur': 15,\n 'heatValueScale': 1,\n 'minOpacity': 0.05,\n 'hitDetect': false\n};\n\nconst COORD = new maptalks.Coordinate(0, 0);\nconst POINT = new maptalks.Point(0, 0);\n\nexport class HeatLayer extends maptalks.Layer {\n\n constructor(id, heats, options) {\n if (!Array.isArray(heats)) {\n options = heats;\n heats = null;\n }\n super(id, options);\n this._heats = heats || [];\n }\n\n getData() {\n return this._heats;\n }\n\n setData(heats) {\n this._heats = heats || [];\n return this._resetData();\n }\n\n addPoint(heat) {\n if (!heat) {\n return this;\n }\n if (heat[0] && Array.isArray(heat[0])) {\n maptalks.Util.pushIn(this._heats, heat);\n } else {\n this._heats.push(heat);\n }\n return this._update(heat);\n }\n\n onConfig(conf) {\n for (const p in conf) {\n if (p === 'gradient') {\n this._updateGradient();\n return this;\n }\n }\n return this;\n }\n\n _updateGradient() {\n const renderer = this._getRenderer();\n if (renderer) {\n renderer.updateGradient();\n renderer.setToRedraw();\n }\n return this;\n }\n\n _resetData() {\n const renderer = this._getRenderer();\n if (renderer) {\n renderer.resetData();\n renderer.setToRedraw();\n }\n return this;\n }\n\n _update(point) {\n const renderer = this._getRenderer();\n if (renderer) {\n renderer.updateData(point);\n renderer.setToRedraw();\n }\n }\n\n isEmpty() {\n if (!this._heats.length) {\n return true;\n }\n return false;\n }\n\n clear() {\n this._heats = [];\n this._resetData();\n this.fire('clear');\n return this;\n }\n\n /**\n * Export the HeatLayer's JSON.\n * @return {Object} layer's JSON\n */\n toJSON(options) {\n if (!options) {\n options = {};\n }\n const json = {\n 'type': this.getJSONType(),\n 'id': this.getId(),\n 'options': this.config()\n };\n const data = this.getData();\n if (options['clipExtent']) {\n let clipExtent = new maptalks.Extent(options['clipExtent']);\n const r = this._getHeatRadius();\n if (r) {\n clipExtent = clipExtent._expand(r);\n }\n const clipped = [];\n for (let i = 0, len = data.length; i < len; i++) {\n if (clipExtent.contains(new maptalks.Coordinate(data[i][0], data[i][1]))) {\n clipped.push(data[i]);\n }\n }\n json['data'] = clipped;\n } else {\n json['data'] = data;\n }\n\n return json;\n }\n\n /**\n * Reproduce a HeatLayer from layer's JSON.\n * @param {Object} json - layer's JSON\n * @return {maptalks.HeatLayer}\n * @static\n * @private\n * @function\n */\n static fromJSON(json) {\n if (!json || json['type'] !== 'HeatLayer') { return null; }\n return new HeatLayer(json['id'], json['data'], json['options']);\n }\n\n\n _getHeatRadius() {\n if (!this._getRenderer()) {\n return null;\n }\n return this._getRenderer()._heatRadius;\n }\n}\n\nHeatLayer.mergeOptions(options);\n\nHeatLayer.registerJSONType('HeatLayer');\n\nHeatLayer.registerRenderer('canvas', class extends maptalks.renderer.CanvasRenderer {\n\n draw() {\n const map = this.getMap(),\n layer = this.layer,\n extent = map.getContainerExtent();\n let maskExtent = this.prepareCanvas(),\n displayExtent = extent;\n if (maskExtent) {\n maskExtent = maskExtent.convertTo(c => map._pointToContainerPoint(c));\n //out of layer mask\n if (!maskExtent.intersects(extent)) {\n this.completeRender();\n return;\n }\n displayExtent = extent.intersection(maskExtent);\n }\n\n if (!this._heater) {\n this._heater = simpleheat(this.canvas);\n }\n this._heater.radius(layer.options['radius'] || this._heater.defaultRadius, layer.options['blur']);\n if (layer.options['gradient']) {\n this._heater.gradient(layer.options['gradient']);\n }\n this._heater.max(layer.options['max']);\n //a cache of heat points' viewpoints.\n if (!this._heatViews) {\n this._heatViews = [];\n }\n\n const heats = layer.getData();\n if (heats.length === 0) {\n this.completeRender();\n return;\n }\n //fix https://github.com/maptalks/maptalks.heatmap/issues/55\n if (this._heater) {\n const width = this._heater._width, height = this._heater._height;\n if (Math.min(width, height) <= 0) {\n this.completeRender();\n return;\n }\n }\n const data = this._heatData(heats, displayExtent);\n this._heater.data(data).draw(layer.options['minOpacity']);\n this.completeRender();\n }\n\n drawOnInteracting() {\n this.draw();\n }\n\n _heatData(heats, displayExtent) {\n const map = this.getMap(),\n layer = this.layer;\n const projection = map.getProjection();\n const data = [],\n r = this._heater._r,\n max = layer.options['max'] === undefined ? 1 : layer.options['max'],\n cellSize = r / 2,\n grid = [],\n panePos = map.offsetPlatform(),\n offsetX = Math.abs(panePos.x) % cellSize,\n offsetY = Math.abs(panePos.y) % cellSize;\n let heat, p, cell, x, y, k;\n // displayExtent = displayExtent.expand(r).convertTo(c => new maptalks.Point(map._containerPointToPrj(c)));\n const { xmin, ymin, xmax, ymax } = displayExtent.expand(r);\n this._heatRadius = r;\n const coord = new maptalks.Coordinate(0, 0);\n for (let i = 0, l = heats.length; i < l; i++) {\n heat = heats[i];\n if (!this._heatViews[i]) {\n this._heatViews[i] = projection.project(coord.set(heat[0], heat[1]));\n }\n p = this._heatViews[i];\n //fix https://github.com/maptalks/maptalks.heatmap/issues/54\n // if (displayExtent.contains(p)) {\n p = map._prjToContainerPoint(p);\n if (p.x < xmin || p.x > xmax || p.y < ymin || p.y > ymax) {\n continue;\n }\n x = Math.floor((p.x - offsetX) / cellSize) + 2;\n y = Math.floor((p.y - offsetY) / cellSize) + 2;\n\n k = (heat[2] !== undefined ? +heat[2] : 0.1) * layer.options['heatValueScale'];\n\n grid[y] = grid[y] || [];\n cell = grid[y][x];\n\n if (!cell) {\n grid[y][x] = [p.x, p.y, k];\n\n } else {\n cell[0] = (cell[0] * cell[2] + (p.x) * k) / (cell[2] + k); // x\n cell[1] = (cell[1] * cell[2] + (p.y) * k) / (cell[2] + k); // y\n cell[2] += k; // cumulated intensity value\n }\n // }\n }\n for (let i = 0, l = grid.length; i < l; i++) {\n if (grid[i] && grid[i].length) {\n for (let j = 0, ll = grid[i].length; j < ll; j++) {\n cell = grid[i][j];\n if (cell) {\n data.push([\n Math.round(cell[0]),\n Math.round(cell[1]),\n Math.min(cell[2], max)\n ]);\n }\n }\n }\n }\n return data;\n }\n\n onZoomEnd() {\n delete this._heatViews;\n super.onZoomEnd.apply(this, arguments);\n }\n\n onResize() {\n super.onResize.apply(this, arguments);\n if (this.canvas) {\n this._heater._width = this.canvas.width;\n this._heater._height = this.canvas.height;\n }\n }\n\n onRemove() {\n this.clearHeatCache();\n delete this._heater;\n }\n\n updateData() {\n }\n\n resetData() {\n this.clearHeatCache();\n }\n\n updateGradient() {\n }\n\n clearHeatCache() {\n delete this._heatViews;\n }\n});\n\nif (typeof CanvasCompatible !== 'undefined') {\n const HeatLayerGLRenderer = class extends CanvasCompatible(maptalks.renderer.LayerAbstractRenderer) {\n drawOnInteracting(event, timestamp, parentContext) {\n this.draw(timestamp, parentContext);\n }\n\n draw(timestamp, parentContext) {\n this.prepareCanvas();\n if (!this._renderer) {\n return;\n }\n const heats = this.layer.getData();\n if (heats.length !== this.pointCount) {\n for (let i = this.pointCount; i < heats.length; i++) {\n this.addPoint(...heats[i]);\n }\n this._updateGeometryData();\n }\n const fbo = parentContext && parentContext.renderTarget && context.renderTarget.fbo;\n this._clearFBO();\n this._geometry.setDrawCount(this.pointCount * 6);\n const map = this.getMap();\n const glRes = map.getGLRes();\n const uniforms = {\n zoomScale: map.getResolution() / glRes,\n projViewModelMatrix: map.projViewMatrix\n };\n this._renderer.render(this._pointShader, uniforms, this._scene, this._fbo);\n this._renderer.render(this._gradientShader, null, this._gradientScene, fbo);\n }\n\n updateData(point) {\n this.addPoint(point[0], point[1], point[2]);\n }\n\n _clearFBO() {\n this.device.clear({\n color: [0, 0, 0, 0],\n depth: 1,\n framebuffer: this._fbo\n });\n }\n\n clearHeatCache() {\n\n }\n\n resetData() {\n this._reset();\n }\n\n updateGradient() {\n this._initGradient();\n }\n\n clear() {\n this._reset();\n super.clear();\n }\n\n _reset() {\n this.pointCount = 0;\n this.bufferIndex = 0;\n this.offsetIndex = 0;\n this.intensityIndex = 0;\n }\n\n initContext() {\n this._initData();\n this._initMesh();\n this._initGradient();\n const viewport = {\n x : 0,\n y : 0,\n width : () => {\n return this.canvas ? this.canvas.width : 1;\n },\n height : () => {\n return this.canvas ? this.canvas.height : 1;\n }\n };\n const extraCommandProps = {\n blend: {\n enable: true,\n func: {\n dst: 1,\n src: 1\n }\n },\n depth: {\n enable: false,\n },\n viewport\n }\n this._pointShader = new reshader.MeshShader({\n name: 'heatmap-point',\n vert,\n frag,\n wgslVert,\n wgslFrag,\n extraCommandProps\n });\n\n this._gradientShader = new reshader.MeshShader({\n name: 'heatmap-gradient',\n vert: gradientVert,\n frag: gradientFrag,\n wgslVert: wgslGradientVert,\n wgslFrag: wgslGradientFrag,\n extraCommandProps: {\n blend: {\n enable: true,\n func: {\n src: 1,\n dst: 'one minus src alpha'\n }\n },\n depth: {\n enable: false,\n },\n viewport\n }\n });\n }\n\n _initData() {\n this.bufferIndex = 0;\n this.offsetIndex = 0;\n this.intensityIndex = 0;\n this.pointCount = 0;\n this.maxPointCount = 1024 * 10;\n\n const { positionBufferData, offsetBufferData, intensityBufferData } = this._initBuffers();\n this.positionBufferData = positionBufferData;\n this.offsetBufferData = offsetBufferData;\n this.intensityBufferData = intensityBufferData;\n }\n\n _initBuffers() {\n const map = this.getMap();\n const isWebGPU = map.getRenderer().isWebGPU();\n const vertexSize = 2;\n const offsetSize = 2;\n const intensitySize = 1;\n const positionBufferData = new Float32Array(\n this.maxPointCount * vertexSize * 6\n );\n const offsetBufferData = new Float32Array(\n this.maxPointCount * offsetSize * 6\n );\n const intensityBufferData = isWebGPU ? new Float32Array(\n this.maxPointCount * intensitySize * 6\n ) : new Uint8Array(\n this.maxPointCount * intensitySize * 6\n );\n return { positionBufferData, offsetBufferData, intensityBufferData };\n }\n\n _initMesh() {\n this._renderer = new reshader.Renderer(this.device);\n this._geometry = new reshader.Geometry(\n {\n aPosition: this.positionBufferData,\n aOffset: this.offsetBufferData,\n aIntensity: this.intensityBufferData\n },\n null,\n this.pointCount * 6,\n {\n positionSize: 2\n }\n );\n this._geometry.generateBuffers(this.device);\n this._mesh = new reshader.Mesh(this._geometry);\n this._scene = new reshader.Scene([this._mesh]);\n const canvas = this.canvas;\n const color = this.device.texture({\n type: 'float16',\n min: 'nearest',\n mag: 'nearest',\n width: canvas.width,\n height: canvas.height\n });\n this._fbo = this.device.framebuffer({\n width: canvas.width,\n height: canvas.height,\n colors: [color],\n colorFormat: 'rgba',\n depthStencil: false\n });\n\n const quad = new Int8Array([\n -1, -1, 0, 1, 1, -1, 0, 1, -1, 1, 0, 1, -1, 1, 0, 1, 1, -1, 0, 1, 1, 1, 0, 1\n ]);\n this._gradientGeometry = new reshader.Geometry(\n {\n aPosition: quad\n },\n null,\n 0,\n {\n positionSize: 4\n }\n );\n this._gradientGeometry.generateBuffers(this.device);\n this._gradientMesh = new reshader.Mesh(this._gradientGeometry);\n this._gradientScene = new reshader.Scene([this._gradientMesh]);\n }\n\n _initGradient() {\n const gradientData = gradient(this.layer.options['gradient']);\n if (this._gradientTexture) {\n if (this._gradientTexture.update) {\n this._gradientTexture.update(gradientData);\n } else {\n this._gradientTexture(gradientData);\n }\n } else {\n this._gradientTexture = this.device.texture(gradientData);\n }\n this._gradientMesh.setUniform('source', this._fbo);\n }\n\n addVertex(x, y, xs, ys, intensity) {\n const map = this.getMap();\n const glRes = map.getGLRes();\n COORD.set(x, y);\n const point = map.coordToPointAtRes(COORD, glRes, POINT);\n x = point.x;\n y = point.y;\n this.positionBufferData[this.bufferIndex++] = x;\n this.positionBufferData[this.bufferIndex++] = y;\n this.offsetBufferData[this.offsetIndex++] = xs;\n this.offsetBufferData[this.offsetIndex++] = ys;\n this.intensityBufferData[this.intensityIndex++] = intensity * 255;\n }\n\n addPoint(x, y, intensity) {\n const size = this.layer.options['radius'] || 25;\n if (intensity == null) {\n intensity = 0.2;\n }\n const max = this.layer.options['max'] || 1;\n intensity /= max;\n this._check();\n const s = size;\n this.addVertex(x, y, -s, -s, intensity);\n this.addVertex(x, y, +s, -s, intensity);\n this.addVertex(x, y, -s, +s, intensity);\n this.addVertex(x, y, -s, +s, intensity);\n this.addVertex(x, y, +s, -s, intensity);\n this.addVertex(x, y, +s, +s, intensity);\n return (this.pointCount += 1);\n }\n\n _check() {\n if (this.pointCount >= this.maxPointCount - 1) {\n this.maxPointCount += 1024 * 10;\n const { positionBufferData, offsetBufferData, intensityBufferData } = this._initBuffers();\n for (let i = 0; i < this.bufferIndex; i++) {\n positionBufferData[i] = this.positionBufferData[i];\n offsetBufferData[i] = this.offsetBufferData[i];\n }\n for (let i = 0; i < this.intensityIndex; i++) {\n intensityBufferData[i] = this.intensityBufferData[i];\n }\n this.positionBufferData = positionBufferData;\n this.offsetBufferData = offsetBufferData;\n this.intensityBufferData = intensityBufferData;\n this._updateGeometryData();\n }\n }\n\n _updateGeometryData() {\n this._geometry.updateData('aPosition', this.positionBufferData);\n this._geometry.updateData('aOffset', this.offsetBufferData);\n this._geometry.updateData('aIntensity', this.intensityBufferData);\n }\n\n onResize(params) {\n if (this._fbo && this.canvas) {\n const canvas = this.canvas;\n if (this._fbo.width !== canvas.width || this._fbo.height !== canvas.height) {\n this._fbo.resize(canvas.width, canvas.height);\n }\n }\n super.onResize(params);\n }\n\n onRemove() {\n this._reset();\n if (this._pointShader) {\n this._pointShader.dispose();\n delete this._pointShader;\n }\n if (this._gradientShader) {\n this._gradientShader.dispose();\n delete this._gradientShader;\n }\n if (this._gradientTexture) {\n this._gradientTexture.destroy();\n delete this._gradientTexture;\n }\n if (this._mesh) {\n this._mesh.dispose();\n delete this._mesh;\n }\n if (this._geometry) {\n this._geometry.dispose();\n delete this._geometry;\n }\n if (this._fbo) {\n this._fbo.destroy();\n delete this._fbo;\n }\n if (this._gradientMesh) {\n this._gradientMesh.dispose();\n delete this._gradientMesh;\n }\n if (this._gradientGeometry) {\n this._gradientGeometry.dispose();\n delete this._gradientGeometry;\n }\n delete this.positionBufferData;\n delete this.offsetBufferData;\n delete this.intensityBufferData;\n delete this._renderer;\n delete this._scene;\n delete this._gradientScene;\n super.onRemove();\n }\n };\n HeatLayer.registerRenderer('gl', HeatLayerGLRenderer);\n HeatLayer.registerRenderer('gpu', HeatLayerGLRenderer);\n}\n\nfunction gradient(grad) {\n // create a 256x1 gradient that we'll use to turn a grayscale heatmap into a colored one\n const canvas = document.createElement('canvas'),\n ctx = canvas.getContext('2d', {willReadFrequently: true}),\n gradient = ctx.createLinearGradient(0, 0, 0, 256);\n\n canvas.width = 256;\n canvas.height = 1;\n\n for (const i in grad) {\n gradient.addColorStop(+i, grad[i]);\n }\n\n ctx.fillStyle = gradient;\n ctx.fillRect(0, 0, 1, 256);\n\n return {\n data: ctx.getImageData(0, 0, 1, 256).data,\n width: canvas.width,\n height: canvas.height\n };\n}\n"],"names":["simpleheat","canvas","this","_canvas","document","getElementById","_ctx","getContext","_width","width","_height","height","_max","_data","module","exports","prototype","defaultRadius","defaultGradient","data","max","add","point","push","clear","radius","r","blur","undefined","circle","_circle","_createCanvas","ctx","r2","_r","shadowOffsetX","shadowOffsetY","shadowBlur","shadowColor","beginPath","arc","Math","PI","closePath","fill","resize","gradient","grad","createLinearGradient","i","addColorStop","fillStyle","fillRect","_grad","getImageData","draw","minOpacity","clearRect","p","len","length","globalAlpha","drawImage","colored","_colorize","putImageData","pixels","j","createElement","constructor","COORD","maptalks","Coordinate","POINT","Point","HeatLayer","Layer","id","heats","options","Array","isArray","super","_heats","getData","setData","_resetData","addPoint","heat","Util","pushIn","_update","onConfig","conf","_updateGradient","renderer","_getRenderer","updateGradient","setToRedraw","resetData","updateData","isEmpty","fire","toJSON","json","type","getJSONType","getId","config","clipExtent","Extent","_getHeatRadius","_expand","clipped","contains","static","_heatRadius","mergeOptions","heatValueScale","hitDetect","registerJSONType","registerRenderer","CanvasRenderer","map","getMap","layer","extent","getContainerExtent","maskExtent","prepareCanvas","displayExtent","convertTo","c","_pointToContainerPoint","intersects","completeRender","intersection","_heater","_heatViews","min","_heatData","drawOnInteracting","projection","getProjection","cellSize","grid","panePos","offsetPlatform","offsetX","abs","x","offsetY","y","cell","k","xmin","ymin","xmax","ymax","expand","coord","l","project","set","_prjToContainerPoint","floor","ll","round","onZoomEnd","apply","arguments","onResize","onRemove","clearHeatCache","CanvasCompatible","HeatLayerGLRenderer","LayerAbstractRenderer","event","timestamp","parentContext","_renderer","pointCount","_updateGeometryData","fbo","renderTarget","context","_clearFBO","_geometry","setDrawCount","glRes","getGLRes","uniforms","zoomScale","getResolution","projViewModelMatrix","projViewMatrix","render","_pointShader","_scene","_fbo","_gradientShader","_gradientScene","device","color","depth","framebuffer","_reset","_initGradient","bufferIndex","offsetIndex","intensityIndex","initContext","_initData","_initMesh","viewport","extraCommandProps","blend","enable","func","dst","src","reshader","MeshShader","name","vert","frag","wgslVert","wgslFrag","maxPointCount","positionBufferData","offsetBufferData","intensityBufferData","_initBuffers","isWebGPU","getRenderer","Float32Array","Uint8Array","Renderer","Geometry","aPosition","aOffset","aIntensity","positionSize","generateBuffers","_mesh","Mesh","Scene","texture","mag","colors","colorFormat","depthStencil","quad","Int8Array","_gradientGeometry","_gradientMesh","gradientData","willReadFrequently","_gradientTexture","update","setUniform","addVertex","xs","ys","intensity","coordToPointAtRes","size","_check","s","params","dispose","destroy"],"mappings":"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"} --------------------------------------------------------------------------------