【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来绘制色卡和风速热力图,以及整个过程中所涉及到的数据处理。

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/243488.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

关于pytorch中的dim的理解

今天碰到一个代码看起来很简单&#xff0c;但是细究原理又感觉好像不太通不太对劲&#xff0c;就是多维tensor数据的操作&#xff0c;比如&#xff1a;y.sum(dim2)&#xff0c;乍一看很简单数据相加操作&#xff0c;但是仔细一想&#xff0c;这里在第3维度的数据到底是横向相加…

什么是前端开发中的跨站请求伪造(CSRF)攻击?如何防止它?

聚沙成塔每天进步一点点 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 欢迎来到前端入门之旅&#xff01;感兴趣的可以订阅本专栏哦&#xff01;这个专栏是为那些对Web开发感兴趣、刚刚踏入前端领域的朋友们量身打造的。无论你是完全的新手还是有一些基础的开发…

dell 恢复系统

戴尔笔记本电脑_台式电脑_服务器_电脑配件_戴尔官方网站 | Dell dell 官方渠道与下载&#xff0c;输入自己dell主机上的服务编码,下载dell的恢复工具回来安装好&#xff0c; 如何下载和使用戴尔操作系统恢复映像 | Dell 中国

MySQL之DQL语句

文章目录 DQL语句指定查询查询全部查询部分数据别名查询使用order by子句拼接查询去重查询WHERE – 条件过滤模糊查询JOIN – 多表关联求和查询排序查询统计查询分页查询 DQL语句 DQL&#xff08;Data Query Language&#xff09;查询数据 操作查询&#xff1a;select简单的查…

Spark集群部署

1.5 Spark集群部署 1.5.1 Spark部署模式 Local 多用于本地测试&#xff0c;如在eclipse&#xff0c;idea中写程序测试等。 Standalone 是Spark自带的一个资源调度框架&#xff0c;它支持完全分布式。 Yarn 生态圈里面的一个资源调度框架&#xff0c;Spark也是可以基于Yarn来…

Python:将numpy数据表示成base64

安装Pybase64库 pip install pybase64conda install pybase64 示例 在Python中&#xff0c;你可以使用numpy库和base64库将numpy数组编码为Base64字符串。以下是一个示例&#xff1a; import numpy as np import base64 # 创建一个numpy数组 data np.array([1, 2…

使用Netropy广域网模拟器测试简化SD-WAN测试

来源&#xff1a;艾特保IT 虹科干货丨使用Netropy广域网模拟器测试简化SD-WAN测试 原文链接&#xff1a;https://mp.weixin.qq.com/s/k5-5Ske9zOMzyx4e3JmtSw 欢迎关注虹科&#xff0c;为您提供最新资讯&#xff01; 文章速览&#xff1a; -为什么需要WAN模拟器&#xff1f;…

AttributeError: ‘BaichuanTokenizer‘ object has no attribute ‘sp_model‘

在使用baihcuan模型进行微调或推理时&#xff0c;遇到AttributeError: BaichuanTokenizer object has no attribute sp_model,针对这个问题issues204上进行了讨论。下面是亲身实践过的方法。 1. 问题 2. 解决方法 降低transformers版本 参考&#xff1a; CUDA:11.6 transfor…

Redis第1讲——入门简介

Java并发编程的总结和学习算是告一段落了&#xff0c;这段时间思来想去&#xff0c;还是决定把Redis再巩固和学习一下。毕竟Redis不论是在面试还是实际应用中都是极其重要的&#xff0c;在面试中诸如Redis的缓存问题、热key、大key、过期策略、持久化机制等&#xff1b;还有在实…

智能优化算法应用:基于鸽群算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于鸽群算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于鸽群算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.鸽群算法4.实验参数设定5.算法结果6.参考文献7.MA…

12.12年末大促,退换货寄件5元起 !

促销新闻报道&#xff1a; 在双十二促销季&#xff0c;闪侠惠递携手圆通、申通、中通、京东、德邦推出了一系列寄件促销活动&#xff01;在这场活动中&#xff0c;退换货运费贵&#xff0c;你该怎么办&#xff1f;从今天开始&#xff0c;闪侠惠递和五大物流企业为您带来了一场…

架构简洁之道有感,谈谈软件组件聚合的张力

配图由腾讯混元助手生成 这篇文章介绍了软件架构设计中组件设计思想&#xff0c;围绕“组件间聚合的张力”这个有意思的角度&#xff0c;介绍了概念&#xff0c;并且结合架构设计示例对这个概念进行了进一步阐述。 组件聚合&#xff1f;张力&#xff1f;这标题&#xff0c;有种…

7+PPI+机器学习+实验,非肿瘤结合建模筛选生物标志物,可升级

今天给同学们分享一篇生信文章“Identification of diagnostic biomarkers and therapeutic targets in peripheral immune landscape from coronary artery disease”&#xff0c;这篇文章发表在J Transl Med期刊上&#xff0c;影响因子为7.4。 结果解读&#xff1a; 外周血中…

docker-compose 单机容器编排

docker-compose 单机容器编排 Dockerfile&#xff1a;先配置好的文件&#xff0c;然后bulid&#xff0c;镜像容器。 docker-compose 既可以基于dockerfile&#xff0c;也可以基于镜像&#xff0c;一键式拉起镜像和容器。 docker-compose 核心就是yml文件&#xff0c;可以定义…

idea__SpringBoot微服务11——整合Druid数据源(新依赖)(新注解)

整合JDBC 一、导入依赖二、配置Druid————————创作不易&#xff0c;如觉不错&#xff0c;随手点赞&#xff0c;关注&#xff0c;收藏(*&#xffe3;︶&#xffe3;)&#xff0c;谢谢~~ 接着 第10的 新注解&#xff1a; ConfigurationProperties ConfigurationPropert…

介绍一款上传漏洞fuzz字典生成工具

介绍一款上传漏洞fuzz字典生成工具 1.工具概述2.安装3.参数解析4.使用案例1.工具概述 upload-fuzz-dic-builder是一个上传漏洞fuzz字典生成脚本,生成时给的上传点相关信息越详细,生成的字典越精确 upload-fuzz-dic-builder 2.安装 克隆项目: git clone git@github.com:c…

TCP/IP详解——TCP 协议

文章目录 一、传输层协议1. TCP1.1 TCP 的字节流1.2 TCP 端口号1.3 TCP 头部1.4 TCP 选项部分字段1.5 TCP 三次握手1.6 TCP 三次握手不成功1.6.1 TCP 拒绝&#xff08;被RST重置&#xff09;1.6.2 TCP 半连接1.6.3 TCP 连接无响应 1.7 TCP 传输过程及原理1.7.1 TCP 传输过程1.7…

现代雷达车载应用——第2章 汽车雷达系统原理 2.4节 雷达波形和信号处理

经典著作&#xff0c;值得一读&#xff0c;英文原版下载链接【免费】ModernRadarforAutomotiveApplications资源-CSDN文库。 2.4 雷达波形和信号处理 对于连续波雷达来说&#xff0c;波形决定了其基本信号处理流程以及一些关键功能。本节将以FMCW波形为例&#xff0c;讨论信号…

【MySQL命令】show slave status\G 超详细全面解释

这个命令是DBA日常运维中常用来查看主从状态的命令&#xff0c;很多备份&#xff0c;监控工具也会使用到该命令监控主从状态是否正常&#xff0c;主从延迟&#xff0c;获取位点信息等。作为常用日常命令&#xff0c;一定要完全理解该命令的输出。今天主要结合 官方文档 和 实际…

iptables详解

1、介绍 iptables 是一个在 Linux 系统上用于配置和管理防火墙规则的工具。它允许系统管理员定义数据包的过滤规则、网络地址转换&#xff08;NAT&#xff09;规则和数据包的网络地址和端口的转发规则。iptables 提供了非常灵活和强大的功能&#xff0c;可以用于保护网络安全、…