【Canvas】绘制风速热力图

2023-12-14 10:36
文章标签 绘制 canvas 力图 风速

本文主要是介绍【Canvas】绘制风速热力图,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言

 📫 大家好,我是南木元元,热衷分享有趣实用的文章,希望大家多多支持,一起进步!

 🍅 个人主页:南木元元


目录

风速热力图

前期工作

数据准备

数据稀疏问题

双线性插值

绘制色卡

绘制热力图

ImageData对象

获取颜色列表

填充像素

结语 


风速热力图

开始之前,大家先来了解一下风速热力图的概念。

风速热力图:用于显示区域内风速的分布情况。它通过在图表上使用颜色编码来表示风速的强度,可以更好地展示风速的变化规律。

风速热力图的特点就是用不同的颜色来表示风速大小,比如较高的风速会用较深的颜色表示,而较低的风速则用较浅的颜色表示。

前期工作

在上篇文章中,我们已经使用canvas绘制出了风场的空间分布图,效果如下:

本文就来聊一下如何在风场上叠加一个风速热力图,最终的效果如下:

和之前相比,还多了一个最左边的色卡,色卡用于表示不同数值和颜色的映射关系。

数据准备

绘制色卡需要准备两组数据:

  • 数值列表
  • 颜色值列表
//存储色卡数据
let heatmapLegend = {colorList: [],valueList: []
};
//数值列表
heatmapLegend.valueList = [6, 7, 8, ..., 65];
//颜色值列表
heatmapLegend.colorList = [[ 0, 0, 127, 255 ], [ 0, 0, 160, 255 ], [ 0, 0, 194, 255 ], ..., [ 160, 0, 0, 255 ]];

上述两个数组中的数据是一一对应的关系,如数值6对应rgba颜色值[ 0, 0, 127, 255 ]。

还需要的当然是网格点上的风速数据了,如下:

[8.9, 10.3, 11.1, 13.6, 16.4, 17.8, 19.6, 16.6, 13.9, 14,  4.6, 8.1, 10.6, 12.6, 12.6, 18.1, 18, 20.1, 23, 27.2, ... 
]

我们的网格是10 * 12的维度,所以是120个格点数据。

数据稀疏问题

其实目前还存在一个问题,就是当前的数据比较稀疏和分散,如果直接进行数值到颜色的映射,那么只会在每个网格点上形成颜色点,无法渲染出一个平滑的热力图,这就需要我们进行相应的插值,来得到比较密集的点。

什么是插值呢?

简单来说,插值指利用已知的点来“猜”未知的点。

双线性插值

我们这里采取双线性插值算法,思路也很简单,就是在两个方向分别进行一次线性插值。

双线性插值的目的就是为了得到密集的数据,从而渲染出高精度、平滑的热力图。具体的插值过程不是本文的重点,这里就不再详细展开,感兴趣的可以去看一篇文章为你讲透双线性插值。

绘制色卡

绘制色卡可以分成两部分,一部分是渲染左侧的颜色列表,另一部分是渲染右侧的文字和背景区。大体思路就是循环遍历颜色列表,使用fillRect填充矩形的方式去渲染每个颜色方块,循环遍历数值列表,使用fillText绘制文字的方式去绘制字体。

// 色卡配置项
let colorCard = {posX: 10,//色卡起始x坐标posY: 20,//色卡起始y坐标width: 5,//每个方块宽度height: 5//每个方块高度
}
// 渲染左侧颜色列表
for (let i = 0; i < this.heatmapLegend.colorList.length; i++) {this.ctx.fillStyle ="rgba(" +this.heatmapLegend.colorList[i][0] +"," +this.heatmapLegend.colorList[i][1] +"," +this.heatmapLegend.colorList[i][2] +"," +this.heatmapLegend.colorList[i][3] +")";this.ctx.fillRect(colorCard.posX,colorCard.posY + colorCard.height * i,colorCard.width,colorCard.height);
}
// 渲染右侧文字背景区
this.ctx.fillStyle = "rgb(0, 0, 0)";
this.ctx.fillRect(colorCard.posX + colorCard.width,colorCard.posY,colorCard.width,colorCard.height * this.heatmapLegend.colorList.length
);
// 渲染右侧文字
this.ctx.fillStyle = "rgb(255, 255, 255)";
this.ctx.font = 5 + "px Microsoft YaHei";
for (let i = 0; i < this.heatmapLegend.valueList.length; i++) {this.ctx.fillText(this.heatmapLegend.valueList[i],colorCard.posX + (3 * colorCard.width) / 2,colorCard.posY + colorCard.height * i);
}

下图就是我们渲染出的色卡:

绘制热力图

在正式绘制热力图之前,我们需要了解下canvas的ImageData对象。

ImageData对象

MDN上的定义。

ImageData描述canvas元素的一个隐含像素数据的区域。用于表示画布上的像素数据,可以通过获取和设置像素值来进行图像处理。

它有三个属性:

  • data:描述了一个一维数组,包含以 RGBA 顺序的数据,数据使用0~255的整数表示。
  • width:ImageData的宽度
  • height:ImageData的高度

接下来看看如何使用它,现在我们想要实现下面的效果。

 代码如下:

let canvas = document.getElementById("canvas");
let ctx = canvas.getContext("2d");
//创建一个新的、空白的100*100的ImageData对象
let imgData = ctx.createImageData(100, 100);
//每个像素都用红色去填充
for (let i = 0; i < imgData.data.length; i += 4) {imgData.data[i+0] = 255;imgData.data[i+1] = 0;imgData.data[i+2] = 0;imgData.data[i+3] = 255;
}
ctx.putImageData(imgData,10,10);

上述代码创建了一个100*100像素的ImageData对象,每个像素被设置为红色,是不是很简单。

获取颜色列表

看了上面的例子,不难发现,绘制热力图,我们只需要得到每个像素的颜色值,然后去填充即可。

//存储ImageData对象的每个像素对应的颜色
let colorValue = [];
getColorValue() {//data为插值后的风速数据for (let i = 0; i < data.length; i++) {let color = this.getColor(data[i]);colorValue.push(color[0]);colorValue.push(color[1]);colorValue.push(color[2]);colorValue.push(color[3]);}
}//获取对应数值的颜色值
getColor(value) {let length = heatmapLegend.valueList.length;let color = [255, 255, 255, 0];//超过数值列表中最大值的数值为该最大值if (value >= heatmapLegend.valueList[length - 1]) {return heatmapLegend.colorList[length - 1];//小于数值列表中最小值的数值为0} else if (value < heatmapLegend.valueList[0]) {return color;}//根据值去查找颜色for (let i = 0; i < length; i++) {if (value < heatmapLegend.valueList[i]) {color = heatmapLegend.colorList[i];break;}}return color;
}

通过上述代码,我们可以得到大致如下的数据:

[0, 0, 227, 255, 0, 6, 227, 255, ...]

填充像素

最后,遍历上述数组,用对应颜色的像素去填充我们新建的ImageData对象就可以了。

//绘制热力图
drawHeatmap() {// 热力图的宽高为整个canvas的宽高减去边上一圈格子的宽高let heatmapWidth = this.canvas2d.width - 2 * this.offsetX;let heatmapHeight = this.canvas2d.height - 2 * this.offsetY;let imgData = this.ctx.createImageData(heatmapWidth, heatmapHeight);for (let i = 0; i < imgData.data.length; i += 4) {imgData.data[i + 0] = colorValue[i + 0];imgData.data[i + 1] = colorValue[i + 1];imgData.data[i + 2] = colorValue[i + 2];imgData.data[i + 3] = colorValue[i + 3];}this.ctx.putImageData(imgData, this.offsetX, this.offsetY);
}

至此,热力图就已经绘制完成了:

再叠加上之前绘制的风场就可以得到我们想要的最终效果了。

结语 

本文分享了如何使用canvas来绘制色卡和风速热力图,以及整个过程中所涉及到的数据处理。

🔥如果此文对你有帮助的话,欢迎💗关注、👍点赞、⭐收藏✍️评论支持一下博主~ 

这篇关于【Canvas】绘制风速热力图的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/492189

相关文章

基于Canvas的Html5多时区动态时钟实战代码

《基于Canvas的Html5多时区动态时钟实战代码》:本文主要介绍了如何使用Canvas在HTML5上实现一个多时区动态时钟的web展示,通过Canvas的API,可以绘制出6个不同城市的时钟,并且这些时钟可以动态转动,每个时钟上都会标注出对应的24小时制时间,详细内容请阅读本文,希望能对你有所帮助...

使用Python绘制蛇年春节祝福艺术图

《使用Python绘制蛇年春节祝福艺术图》:本文主要介绍如何使用Python的Matplotlib库绘制一幅富有创意的“蛇年有福”艺术图,这幅图结合了数字,蛇形,花朵等装饰,需要的可以参考下... 目录1. 绘图的基本概念2. 准备工作3. 实现代码解析3.1 设置绘图画布3.2 绘制数字“2025”3.3

使用Python绘制可爱的招财猫

《使用Python绘制可爱的招财猫》招财猫,也被称为“幸运猫”,是一种象征财富和好运的吉祥物,经常出现在亚洲文化的商店、餐厅和家庭中,今天,我将带你用Python和matplotlib库从零开始绘制一... 目录1. 为什么选择用 python 绘制?2. 绘图的基本概念3. 实现代码解析3.1 设置绘图画

Python绘制土地利用和土地覆盖类型图示例详解

《Python绘制土地利用和土地覆盖类型图示例详解》本文介绍了如何使用Python绘制土地利用和土地覆盖类型图,并提供了详细的代码示例,通过安装所需的库,准备地理数据,使用geopandas和matp... 目录一、所需库的安装二、数据准备三、绘制土地利用和土地覆盖类型图四、代码解释五、其他可视化形式1.

如何用Python绘制简易动态圣诞树

《如何用Python绘制简易动态圣诞树》这篇文章主要给大家介绍了关于如何用Python绘制简易动态圣诞树,文中讲解了如何通过编写代码来实现特定的效果,包括代码的编写技巧和效果的展示,需要的朋友可以参考... 目录代码:效果:总结 代码:import randomimport timefrom math

【WebGPU Unleashed】1.1 绘制三角形

一部2024新的WebGPU教程,作者Shi Yan。内容很好,翻译过来与大家共享,内容上会有改动,加上自己的理解。更多精彩内容尽在 dt.sim3d.cn ,关注公众号【sky的数孪技术】,技术交流、源码下载请添加微信号:digital_twin123 在 3D 渲染领域,三角形是最基本的绘制元素。在这里,我们将学习如何绘制单个三角形。接下来我们将制作一个简单的着色器来定义三角形内的像素

Flutter 进阶:绘制加载动画

绘制加载动画:由小圆组成的大圆 1. 定义 LoadingScreen 类2. 实现 _LoadingScreenState 类3. 定义 LoadingPainter 类4. 总结 实现加载动画 我们需要定义两个类:LoadingScreen 和 LoadingPainter。LoadingScreen 负责控制动画的状态,而 LoadingPainter 则负责绘制动画。

利用matlab bar函数绘制较为复杂的柱状图,并在图中进行适当标注

示例代码和结果如下:小疑问:如何自动选择合适的坐标位置对柱状图的数值大小进行标注?😂 clear; close all;x = 1:3;aa=[28.6321521955954 26.2453660695847 21.69102348512086.93747104431360 6.25442246899816 3.342835958564245.51365061796319 4.87

YOLOv8/v10+DeepSORT多目标车辆跟踪(车辆检测/跟踪/车辆计数/测速/禁停区域/绘制进出线/绘制禁停区域/车道车辆统计)

01:YOLOv8 + DeepSort 车辆跟踪 该项目利用YOLOv8作为目标检测模型,DeepSort用于多目标跟踪。YOLOv8负责从视频帧中检测出车辆的位置,而DeepSort则负责关联这些检测结果,从而实现车辆的持续跟踪。这种组合使得系统能够在视频流中准确地识别并跟随特定车辆。 02:YOLOv8 + DeepSort 车辆跟踪 + 任意绘制进出线 在此基础上增加了用户

使用matplotlib绘制散点图、柱状图和饼状图-学习篇

一、散点图 Python代码如下: num_points = 100x = np.random.rand(num_points) #x点位随机y = np.random.rand(num_points) #y点位随机colors = np.random.rand(num_points) #颜色随机sizes = 1000 * np.random.rand(num_points) # 大