气象可视化之windy方案的技术分析

共 7658字,需浏览 16分钟

 ·

2022-08-07 19:38

一  前言

      “颜值即正义”的时代,windy(https://www.windy.com/)是气象领域2D可视化效果和用户体验最好的站点,没有之一,是当前气象可视化领域的标杆。windy的成功在于实际解决了气象系统中常见的高精度、大范围格点数据文件大导致网络传输慢、可视化效果保守不够美观等问题。windy方案本质上的技术不是很复杂,因此国内气象公司的windy仿产品纷纷推出,技术开发社区也时常有关于windy技术方案的讨论,本篇笔者就此做一些较全面的技术讨论和分析。

a32cda16480da47669108fb317baed75.webp

windy原版站点

9fceb16635345061d02940d66f38577e.webp

windy仿:中科天机

6fd7d7e80645977dba962d356b3fd734.webp

windy仿:九方科技

二  技术实现

      windy站点给人感觉就是简洁,也没有明显的网络卡顿和渲染延迟,主要是其改进了两个技术实现的,即灰度图切片和灰度图渲染技术,本章节做些技术方案简要说明,为叙述方便,梳理其基本的数据逻辑如下:

c76d7113da14ab856c914d2940fe69a8.webp

windy技术方案基本数据处理逻辑

2.1 格点切片

      传统气象业务系统通常将整个格网文件传输到前台,随着格网的地理分辨率变高,格网地理区域范围的变大,这个数据文件通常很大,例如,全球南北纬度是-90-90,东西经度是-180-180,假设地理分辨率是1°,那么格点数量=181*361;如果把地理分辨率从1°改成0.1°,那么格点数量=1801*3601,不熟悉气象格点文件的朋友可以将其类比,如gis里的栅格数据,如文件系统里的图片,分辨率越高,文件越大,本质一样。因此,传统方案中将整个数据文件在bs系统中通过网络上传的方式,首要的问题就是极容易出现带宽不足,网络传输瓶颈   

       为了解决该问题,windy借鉴了类似的WebGIS影像金字塔切片技术方案原理,对整张数据文件进行切片。webgis中的影像数据通常都是图像,一般先根据zoom级别建立影像金字塔索引,再以256*256或者512*512大小进行图像切片传输给前台显示。windy基于同样的技术原理,将格点按照zoom级别建立金字塔索引,再以自己的切片宽高组织格点切片,由于格点切片本质是数据,而不是图像,因此windy自己在切片中塞入了图例元数据,其他厂商可能是257*257等,边缘多一像素希望在瓦片拼接时尽量平滑点,格点数值的切片差异化与webgis里的图像切片些许不太一样

        格点切片方案将庞大气象格点文件切片化,是解决网络传输的利器。

2.2 格点切片灰度图化

       格点数值并不是图像的像素值,于是windy对切片中格点值建立与图像值的映射关系,即统计格点切片中格点数值范围[min,max],然后将其映射到图像像素值的[0,255]区间。例如温度Tile,该Tile中最低温度是0,最高温度是30,则将0映射到图像值0,30映射到图像值255,映射公式如下:

// 格点Tile数值区间const minVal = 0, maxVal = 30;const imageValue = Math.round((gridValue - minVal)/(maxVal - minVal)*255.0);

       细心的读者会发现,这个映射关系是一个有损压缩,此处暂且不表。但经过映射,每个格点值都有对应的一个像素值,windy将其编码到图像的R通道,所以windy的图像看着都是红色的,而其他厂商都编码到rgb通道,每个通道值相同,所以图像看着是灰色的,即所谓的“灰度图”。

c97f0100449b79d22f0335098a29f45e.webp

windy的格点切片

85696d9a2ee2a0d8e0ebac6f2bae8195.webp

  windy仿的格点切片

       对于风这样的u、v矢量,则将u转码图像编码到r通道,v转码到图像编码到g通道,因此,风的格点切片看起来是彩色的如下图:

13b3735816542b67d531ef8c2ce7cfd4.webp

uv编码rg通道

        格点图像编码将一方面格点Tile进一步将压缩至只有原先的几分之一大小(float64转uint8),进一步优化了网络传输能力;另外一方面,图像对webgl是很友好的格式,是直接就能认的“纹理”数据,渲染比较方便。

2.3 数据精度问题优化

      上一章节根据公式已经表达出了格点切片转图像切片是线性有损压缩的,即当数值范围的min与max差距越大,则压缩后的数据误差会越大。例如将温度数值从-40-50编码到0-255误差会很小,而如果某个气象产品的数值假设在0-2000之间,将其压缩到0-255区间,格点值为9,10,11的数值都被映射成了1,数据传输到前台就已经失真了。

Math.round((9.0-0)/(2000-0)*255.0)= 1;Math.round((10.0-0)/(2000-0)*255.0)= 1;Math.round((11.0-0)/(2000-0)*255.0)= 1;Math.round((12.0-0)/(2000-0)*255.0)= 2;

        为了在数据压缩和数据精度之间做个平衡,windy采用的是格点tile单独编码方案。地理学认为,彼此距离较近的事物要比距离较远的事物更相似,因此,windy根据地理范围生成的格点tile,单独提取该tile里的格点最小最大值作为编码依据,而不是格点数据中全局的最小最大值,在减少数据误差,提升数据精度上有一些作用,但并不能完全解决这个问题。

3cc89770f0e8bb2afcf90ab065bd8052.webp

距离越近事物越相似

       如上图,假设中国的气象格网文件,温度最低是-20,分布在青藏高原附近,最高数值是40,分布在吐鲁番或长江流域(三伏天),那么整个格网数据的数值范围是[-20,40]。而windy的分块单独编码,例如上图左边Tile是青藏高原范围,数值是[-20,5],上图右边Tile的数值范围可能是[20,40]。根据上文提出的,数据起始终止值差距越大,误差越大的结论,全局数据编码值差距是40-(-20)=60,而单独编码分别是5-(-20)=25,40-20=20,分别编码的值差距都小于全局值差距,对精度提升是有进一步帮助的。

       由于每个tile是单独编码的,前端为了解码,就得每个tile定义不同的元数据,windy直接在数据图像上的第一行单独编码了元数据图例:

73cd7d2295d85385af2e3a75dd05ed09.webp

格点图像附图例元数据

       目前,国内大多windy仿是基于WebGIS的技术做的,即先全局将格点文件转码灰度栅格图像,再基于GIS的切片传输到前台,全局的图像编码导致数据精度的误差会放大不少,类似这样的细节处理优化问题还有很多,但国内的模仿者们并没有在细节上下功夫,国内的windy的效果没有一个能比得上windy的。

6603025f010575db148fe5e9a38bb9c1.webp

windy仿 step1:预先全局转码(误差会放大)

182bff4148f83e2a4770aebe6185d420.webp

windy仿 step2:生成灰度图切片

2.4 灰度图渲染

      上文可知,在完成数据的基本转换,通过网络请求切片,前端得到的是灰度图切片图像,图像里的图例生成渐变的颜色纹理,图像里的编码数值生成数据纹理,基于webgl技术渲染主要分两步骤:先采样格点数值,再根据数值去颜色纹理采样颜色,完成最终的渲染。

    #version 300 es    uniform sampler2D u_data_texture;    uniform sampler2D u_color_texture;    in vec2 v_uv;    out vec4 outColor;    void main() {        // step1 根据uv坐标采样格点数值纹理        float val = texture(u_data_texture, v_uv).r;        // step2 根据格点数值去颜色纹理采样渲染颜色        outColor  = texture(u_color_texture, vec2(val, 0.5));    };

三 优点总结

       windy的数据处理方案是对传统bs气象系统的一次颠覆性创新,是web切片技术尤其是webgis切片技术与webgl渲染技术的融合,优点非常明显:

  • 数据切片:化整为零,解决网络传输瓶颈。

  • 数据压缩:进一步压缩数据体积,优化网络传输。

    数据渲染:不同于传统的canvas技术,webgl渲染更加快速,没有渲染瓶颈,并且更容易实现基于gpu的数据实时插值,在数据的时序动画中更加平滑,而不是轮播图形式。

    时序数据动画渲染

四 技术局限性

       windy方案看起来非常美好,也有很多气象公司跟进,但是类windy方案直接迁移到气象内网专业系统里仍会出现一些问题,这主要是专业气象领域与纯可视化网站的业务要求不同、对数据精度要求不同导致的。

4.1 渲染的业务要求不同

       对于格网面的渲染,目前windy里只支持渐变色渲染,而气象专业系统里可能要支持等值面或格网色斑图。其实纯用渐变色渲染,能比较合理的掩盖一些数据精度不足的问题,毕竟肉眼是不可分辨的;而其他形式的等值面和色斑图是气象系统经典出图的表达形式,一点精度不对很容易导致地图上的可视化和专题图不一致的情况。

b4b4f0ba4802b2d78ffbea836b65c9c2.webp

windy默认渐变渲染   4dcbcefa8a845ef4cf14e1a210059954.webp业务要求等值面渲染   

989792c09f68733e95eddb612d5c7d84.webp

业务要求格点方块渲染   

4.2 数据精度要求不同

       前文已说,类似将float64转uint8的线性有损压缩,牺牲精度前提下,集中注意力到网络传输的优化上,对windy站点本身是可以接受的,主要是因为windy站点气象产品有限,站点列举的几十类,都是线性数据,种类不多且没有指数级产品的出现。即使部分产品例如降雨量,图例也是windy自己的标准,而不是气象行业里的标准,所以看起来比较平滑:

f26c62ab7eb7f808d546a7c2fa653319.webp

windy的降雨量分级

        实际气象业务中,例如1h降雨量精度,数值最低要达到0mm,无降水,最高是50mm,那么按照windy的归一化编码公式0.1mm几乎差一点被编码到和无降水的0是一样的数据编码上,精度的控制极其困难。

// 编码0 mmlet val_0 = (0-0)/(50-0)*255.0; // 0let val_01 = (0.1-0)/(50-0)*255.0; // 0.51 // 取整,0mm被编码到像素值0,0.1mm被编码到像素值1val_0 = Math.round(val_0); // 0val_0 1= Math.round(val_01); // 1// 假如max不等于50,而是等于60,岂不是0和0.1被编码到同一个像素上?

20c63cb54ab18602036e050dfdbbf631.webp

业务系统里的降水量分级


       实际气象业务系统中气象产品种类非常繁多,经常有格点值值域在0至几千,甚至有指数级数值产品,例如地震的能量指数级,类似这种值域差距过大甚至指数级数据,精度丢失是惊人的。当对有损压缩的数据以格点或者等值面显示,对照气象专业的出图工具出的图,基本就对不上了。也就是说,国内windy仿实际是很难应用于实际业务的,至少不能完全覆盖。

4.3 图例精度不足

       windy的渲染分两步,数值纹理采样,颜色纹理采样,但在某些极端情况,根据图例生成的颜色纹理本身就有问题,最终根据颜色纹理采样后显示的颜色肯定就不对了,本小节稍微详细说明下。

       渐变的颜色纹理通常是基于类似下文的代码生成的:

function getGradientColorRamp(normalLegend: Map<number, number[]>{    // eslint-disable-next-line no-undef    const canvas = document.createElement('canvas');    const ctx = canvas.getContext('2d');    const width = 256;    canvas.width = width;    canvas.height = 1;    const gradient = ctx.createLinearGradient(0, 1, width, 1);    for (let [key, value] of normalLegend) {        if (key > 1)            key = 1;        gradient.addColorStop(key, `rgba(${value.join(',')})`);    }    ctx.fillStyle = gradient;    ctx.fillRect(0, 0, width, 1);    return new Uint8Array(ctx.getImageData(0, 0, width, 1).data);}

         正常这段代码是没有问题的,但是考虑一下,假如我们要显示一个指数级产品,图例给我们的是如下的数据内容,每个图例的key差异比较大:

        let legend = new Map();        legend.set(0, [255, 255, 255]);        legend.set(0.0157, [221, 221, 221]);        legend.set(0.032, [0, 160, 246]);        legend.set(0.0654, [0, 236, 236]);        legend.set(0.133, [0, 216, 0]);        legend.set(0.272, [0, 144, 0]);        legend.set(0.556, [255, 255, 0]);        legend.set(1.13, [231, 192, 0]);        legend.set(2.31, [255, 144, 0]);        legend.set(4.72, [255, 0, 0]);        legend.set(9.64, [214, 0, 0]);        legend.set(19.7, [192, 0, 0]);        legend.set(28.1, [255, 0, 240]);

       根据该图例生成的渐变颜色纹理如下:

af8725927d9fad75b737fe28d413fbda.webp

图例生成的颜色纹理(宽度放大)

       图例里要求的数值0是白色,0.0157是灰色,但在最终生成的纹理图像里丝毫没看到白色和灰色的颜色,笔者一开始怀疑是不是数值过于接近,实际图像中有白色和灰色条带肉眼看不到咧?但是实际不是的,图像直接就把这些值舍弃了,因为图例精度不够。

        示例中,图例的key值域在[0,28.1],归一化,并计算颜色纹理所在的像素值位置,其结果依次是:

图例值       归一化               颜色纹理像素位置0             0                     00.0157    0.0005587188612099644     00.032     0.0011387900355871886     00.0654    0.0023274021352313167     10.133     0.004733096085409253      10.272     0.009679715302491104      20.556     0.019786476868327404      51.13      0.04021352313167259      102.31      0.08220640569395017      214.72      0.1679715302491103       439.64      0.34306049822064055      8719.7      0.701067615658363        17928.1      1                        255

       由于颜色纹理长度是256,根本塞不下这个图例,导致前三个图例值都对应同一个颜色纹理位置0处,因此,实际上前2个图例已经没有了,一个像素不可能被赋予三个颜色,最终只有最后一个颜色被保留了。虽然通过不断的增加canvas的长度从而能完整保留图例,但是基本没有厂商去处理图例精度问题,毕竟实际用的少,问题都没碰到,当然更谈不上去解决了。

4.4 前端可视化切片边缘切割感

       由于windy是格点切片,前端webgl会对每个切片单独渲染,熟悉webgl的人应该知道,纹理渲染要设置采样策略,如插值策略是线性还是最近邻,边缘是重复还是clamp,每个tile都是单独渲染,所以tile边缘的连接处由于采样的缘故就很不光滑自然,有明显的“刀切一样的割裂感”,这种现象windy自己也有,但是经过多次优化已经不太明显,由于“血脉传承”的关系,windy有的问题,windy仿们一个不落,由于windy仿们细节处理严重不足,看起就特别不明显和不舒服,割裂感如下图所示:

f1d81063da00b201fb6ca87edb81d1a5.webp

windy原厂:切片边缘的割裂感

ea5231cf30d571509df6f57b75a62bae.webp

windy仿:切片边缘的割裂感

1a98baeb5b854abea6000a71d835ba82.webp

windy仿:片边缘的割裂感

五 总结

       本文分析了windy方案的基本数据处理逻辑,总结了优点和类windy方案的不足,最后做个个人关于windy方案的简单小节:

  • windy方案并不是通用的解决方案。通常数值范围差距不大且线性数据是比较好用,反之就不能用,例如指数级产品。

  • 仅用于专业性较弱的场景下可视化。windy的图例分级是为了美观自定义的,而专业领域为了准确是严格标准的,所以不能完全替代专业性较强的业务场景。有些在windy里好看,移植到专业气象系统上可能很难看。

  • 类windy方案存在数据精度和图例精度问题,导致数据精度要求极其敏感的场景下出现显示错误。

  • windy仿的站点,普遍存在细节处理能力不足的现象,性能、精度、效果没有一个能达标的基本上,不创新就靠仿照,应该是出不了“中国版windy”的。


        由于windy自身的不足,想要在性能、精度、效果上超越它并不是不可能的。例如保留数据切片和格点金字塔索引思想,但是不转码图片,以其他二进制自定义格式返回,前端解码数据构造浮点型纹理,避免图像转码过程中的精度丢失;其他色条精度也改用根据数据实际图例动态生成避免精度不够的丢失等等。

        一味仿照永远不会超过,只有分析别人的问题,思考用什么方案去解决问题,制定新的技术方案、标准、工艺流程、框架才能在另一个维度上实现超越,而我本人正在为此而努力。。。

        更多关于开源GIS架构的技术交流可加qq群:445307545。

浏览 1484
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报