风场可视化:风场数据

451 阅读3分钟

引子

了解 WebGL 基础之后,接着去看获取解析风场数据的逻辑,又遇到问题。

安装 ecCodes

在文章示例源库的说明中,首先要安装 ecCodes ,尝试使用 HomeBrew 但不行。于是就按照 ecCodes 源库的介绍本地进行编译安装。

在进行第 4 步的时候,碰到了问题:

No CMAKE_Fortran_COMPILER could be found.

查询资料说是缺少 gfortran ,可以使用命令查看是否已安装:


which gfortran

有好几种安装方式,我选择下载安装包

解决这个问题后按照指导继续,编译安装成功,版本是 2.23.0 。

执行脚本

执行脚本的时候,出现了错误提示:

grib_set: command not found grib_dump: command not found

但在前面安装的文件夹的 bin 目录下是找到了 grib_set 的执行文件。推断是没有注册到全局路径中。

查看 ecCodes 安装路径是否注册到全局路径中:


echo $PATH

这里碰到的问题是没有注册到全局路径中,设置方式可参考这里

修改示例:


vim ./.bash_profile

进入到编辑模式后,添加下面的内容:


export ECCODE_HOME=/xx/xx/xx/xx/eccodesbuild/bin

export PATH=$PATH:$ECCODE_HOME

保存后,使其生效


source ./.bash_profile

想知道是否生效了,试试指令 grib_set -h ,如果发现没有效果,有可能跟使用的 shell 端有关,可参看这里

数据生成

脚本可以正常执行了,但生成的数据不对:

undefined:1

{"u":,"v":}

查看源库的 issues ,里面也有人提这个问题,试了里面的一些方法,发现这个 pull 的修改可以正常的运行。于是就 fork 了一下把这个修改的内容弄过来了,改了些数据,见 XXHolic/webgl-wind

数据含义

download.sh 脚本中,获取数据解析后,生成可读文件 tmp.json ,来看看这个文件中主要结构和部分数据:


{

"u":{

"messages" : [

[

{

"key" : "name",

"value" : "U component of wind"

},

{

"key" : "Ni",

"value" : 360

},

{

"key" : "Nj",

"value" : 181

},

{

"key" : "values",

"value" : [5.51964, 5.71964, ...]

},

{

"key" : "maximum",

"value" : 103.02

},

{

"key" : "minimum",

"value" : -36.0804

}

]

]

},

"v":{

"messages" : [

[

{

"key" : "name",

"value" : "V component of wind"

},

{

"key" : "getNumberOfValues",

"value" : 65160

},

{

"key" : "values",

"value" : [14.9446, 14.8446, ...]

},

{

"key" : "maximum",

"value" : 80.3446

},

{

"key" : "minimum",

"value" : -66.4554

}

]

]

}

}

看到这些可能会有些疑惑,大气中的气流既有速度也有方向,在数学上可以用一个向量表示。在气象学中,如果知道风的方向和大小,就可以得到表示风的向量,u 分量和 v 分量:


// ws 风力大小 θ 风在数学上的方向描述

u = ws * cos(θ)

v = ws * sin(θ)

97-1

更加详细的介绍见 Wind: u and v Components

接着在 prepare.js 中使用到风数据中的 key 有:

  • Ni 表示在一条纬线上有多少个点,简单的说就是有多少列。

  • Nj 表示在一条经线上有多少个点,简单的说就是有多少行。

  • values 存放分量所有的值。

  • minimum 表示分量的最小值。

  • maximum 表示分量的最大值。

其中 NiNj 决定了生成图片的宽和高,风速大小映射对应颜色主要逻辑如下:


for (let y = 0; y < height; y++) {

for (let x = 0; x < width; x++) {

const i = (y * width + x) * 4;

const k = y * width + ((x + width / 2) % width);

png.data[i + 0] = Math.floor(

(255 * (u.values[k] - u.minimum)) / (u.maximum - u.minimum)

);

png.data[i + 1] = Math.floor(

(255 * (v.values[k] - v.minimum)) / (v.maximum - v.minimum)

);

png.data[i + 2] = 0;

png.data[i + 3] = 255;

}

}

  • i : 使用了 pngjs 插件,颜色使用 RGBA 模式,数组中每连续的 4 个位置存储的是一个点的颜色值,所以 i 变量要是 4 的倍数。

  • k : 用于获取风速大小的索引,先看看 y=0 时,k 的取值变化先从 180 -> 359 递增,然后从 0 -> 179 递增,这样从中间开始取值,猜测是由于这个展示地图是二维的世界地图,返回的数据就是这样的对应规则。

  • 映射方式: 以 maximum - minimum 作为基准,然后计算风速大小相对值 values[k] - minimum ,两个值的比例乘以颜色分量最大值 255 。

参考资料

wastebasket

《JOJO的奇妙冒险 石之海》 最近一下子放出了 12 集,质量还是相当好的。

97-poster