Author: Fenglu Li, Visualization engineer of TalkingData
Editor: Aresn
Welcome to join QQ group to participate in technical discussion: 618308202
InMap is a big data visualization library based on Canvas, which focuses on the visualization of points, lines and planes in the direction of big data. Currently supports scatter, fence, thermal, grid, polymerization and other modes; Committed to making big data visualization easy to use.
Making address:Github.com/TalkingData…(Click a Star to support the author!)
A thermal map is a fancy name, but it’s the equivalent of what we call a density map.
As shown in the figure, the red area represents the high density of the analytical element, while the blue area represents the low density of the analytical element. As long as the points are dense, clustering regions will be formed. See so dazzle effect, is oneself also want to achieve one? Next, I will implement a heat by hand. For the record, the following code snippets are from inMap.
To prepare data
InMap receives latitude and longitude data and needs to map it to the pixel coordinates of canvas, which uses Mercator transformation. The Mercator algorithm is very complex, and we will have a separate article on its principle in the future. After the transformation, you should get something like this:
[{"lng": "116.395645"."lat": 39.929986."count": 6,
"pixel": {// Pixel coordinates"x": 689,
"y": 294}}, {"lng": "121.487899"."lat": 31.249162."count": 10,
"pixel": {// Pixel coordinates"x": 759,
"y": 439}},... ]Copy the code
Now that we have the converted pixel coordinate data (x, y), we can do the following.
Create canvas gradient fill
Create a gradient circle from black to white
let gradient = ctx.createRadialGradient(x, y, 0, x, y, radius);
gradient.addColorStop(0, 'rgba (0,0,0,1)');
gradient.addColorStop(1, 'rgba (0,0,0,0)');
ctx.fillStyle = gradient;
ctx.arc(x, y, radius, 0, Math.PI * 2, true);
Copy the code
- CreateRadialGradient () creates a linear gradient object
- AddColorStop () defines a gradient color band
The effect is as follows:
Set the globalAlpha
Set different Alpha according to different count values, assuming that the Alpha of the largest count is equal to 1 and the Alpha of the smallest count is 0, then I calculate Alpha according to count.
let alpha = (count - minValue) / (maxValue - minValue);
Copy the code
Then our code is as follows:
drawPoint(x, y, radius, alpha) {
letctx = this.ctx; ctx.globalAlpha = alpha; // Set Alpha transparency ctx.beginPath();let gradient = ctx.createRadialGradient(x, y, 0, x, y, radius);
gradient.addColorStop(0, 'rgba (0,0,0,1)');
gradient.addColorStop(1, 'rgba (0,0,0,0)');
ctx.fillStyle = gradient;
ctx.arc(x, y, radius, 0, Math.PI * 2, true);
ctx.closePath();
ctx.fill();
}
Copy the code
The effect is very different from the previous screenshot, you can compare the change in transparency.
Reset canvas canvas color
- GetImageData () copies the pixel data of the specified rectangle on the canvas
- PutImageData () puts the image data back on the canvas:
GetImageData () returns the following data format:
{
"data": {
"0": 0, //R
"1": 128, //G
"2": 0, //B
"3": 255, //Aplah
"4": 0, //R
"5": 128, //G
"6": 0, //B
"Seven": 255, //Aplah
"8": 0."9": 128,
"10": 0."11": 255,
"12": 0."13": 128,
"14": 0."15": 255,
"16": 0."17": 128,
"18": 0."19": 255,
"20": 0."21": 128,
"22": 0...Copy the code
The data returned is a one-dimensional array with four elements each representing a pixel (RGBA) value.
Implement the thermal principle: read the alpha value (transparency) of each pixel and make a color map.
The code is as follows:
let palette = this.getColorPaint(); // Select color panel
let img = ctx.getImageData(0.0, container.width, container.height);
let imgData = img.data;
let max_opacity = normal.maxOpacity * 255;
let min_opacity = normal.minOpacity * 255;
// Weight range
let max_scope = (normal.maxScope > 1 ? 1 : normal.maxScope) * 255;
let min_scope = (normal.minScope < 0 ? 0 : normal.minScope) * 255;
let len = imgData.length;
for (let i = 3; i < len; i += 4) {
let alpha = imgData[i];
let offset = alpha * 4;
if(! offset) {continue;
}
// Map colors
imgData[i - 3] = palette[offset];
imgData[i - 2] = palette[offset + 1];
imgData[i - 1] = palette[offset + 2];
// Range range
if (imgData[i] > max_scope) {
imgData[i] = 0;
}
if (imgData[i] < min_scope) {
imgData[i] = 0;
}
/ / transparency
if (imgData[i] > max_opacity) {
imgData[i] = max_opacity;
}
if(imgData[i] < min_opacity) { imgData[i] = min_opacity; }}// Place the set pixel data back on the canvas
ctx.putImageData(img, 0.0.0.0, container.width, container.height);
Copy the code
Create a color map. A good color map determines the final effect. InMap creates a palette 256px long:
let paletteCanvas = document.createElement('canvas');
let paletteCtx = paletteCanvas.getContext('2d');
paletteCanvas.width = 256;
paletteCanvas.height = 1;
let gradient = paletteCtx.createLinearGradient(0, 0, 256, 1);
Copy the code
The default colors of inMap are as follows:
This. Gradient = {0.25:'RGB (0,0,255)'And 0.55:'RGB (0255, 0)'And 0.85:'yellow'And 1.0:'RGB (0, 255)
};
Copy the code
Set the gradient color to the Palette object
for (let key in gradient) {
gradient.addColorStop(key, gradientConfig[key]);
}
Copy the code
Returns pixel data for palette palette:
return paletteCtx.getImageData(0, 0, 256, 1).data;
Copy the code
The resulting palette looks like this :(looks like a gradient color bar)
Finally, we achieved the following thermal map:
Next day forecast
In the next section, we will focus on the implementation of inMap text avoidance algorithm.