GRIB学习笔记:GRIB2要素场转换为图片

目录

Web实时显示气象要素场一般有两种方式:

  • 预先生成图片,叠加在底图上
  • 加载远程数据,本地绘图

本文主要介绍第二个方法中加载远程数据的一种方法。

概述

GRIB2消息中保存的要素场一般使用JPEG压缩,我们常用的网格是等经纬度网格,数据是一个矩阵形式,因此GRIB2消息中的数据可以看成一张常规的图片。尽管目前尚缺乏有效的解码GRIB2数据的JavaScript库,但我们可以将GRIB2数据转成图片格式传给Web客户端,以图片的方式传递数据。

数据转换

将GRIB2要素场转换为图片,需要考虑如何转换实际数据为图片像素点的颜色值。

我们可以借鉴GRIB2存储数据的方式。为了保证数据的经度,GRIB2的数据段保存的不是实际的数据信息,而是使用一个公式计算后的结果。GRIB2数据的计算公式如下:

$$Y*D^{10}=R+(X1+X2)*2^E$$

其中

  • Y:原始值
  • D:十进制比例因子,为了获得需要的精确度。有符号十六位整数
  • R:参考值,32位浮点数
  • X:内部值,每个记录有自己的位数
  • E:二进制比例因子,用于变长字长打包,有符号十六位整数

每个数据点只有Y和X值不同,所以数据段只存储X值,其他数值保存在数据描述段。

上面的公式比较复杂,在线绘图一般不需要很高的精度,所以可以将上面的公式简化。为了将数据转换为灰度图,将最大值最小值区间分成256份,每个数据值映射到这256个子区间。简化后的公式如下所示:

$$Y=(M_{2}-M_{1})*X/255+M_{1}$$

其中

  • Y:原始值,浮点数
  • M1:数据最小值,浮点数
  • M2:数据最大值,浮点数
  • X:像素点灰度值,整形

使用上述公式,就可以将GRIB2数据转为一张与网格大小相同的灰度图像。为了恢复原始数据,还需要知道M1,M2的值,因此将这些信息写入一个单独的JSON文件,与数据一同发送给客户端,由客户端负责从灰度值恢复原始数据。

实现

使用nuwe_pyeccodes获取GRIB2要素场。下面代码中message_handler代表一个要素场。

import numpy as np

left_lon = message_handler.getDouble('longitudeOfFirstGridPointInDegrees')
right_lon = message_handler.getDouble('longitudeOfLastGridPointInDegrees')
lon_step = message_handler.getDouble('iDirectionIncrementInDegrees')
nx = message_handler.getLong('Ni')

top_lat = message_handler.getDouble('latitudeOfFirstGridPointInDegrees')
bottom_lat = message_handler.getDouble('latitudeOfLastGridPointInDegrees')
lat_step = message_handler.getDouble('jDirectionIncrementInDegrees')
ny = message_handler.getLong('Nj')

values = message_handler.getDoubleArray('values')
values = values.reshape(ny, nx)

min_value = np.max(values)
max_value = np.min(values)

values = (values - min_value) / (max_value-min_value) * 255

使用PIL生成图片文件

from PIL import Image

im = Image.fromarray(values)
im = im.convert("L")
im.save("t.png")

生成对应的JSON描述文件

import json

image_info = {
    'min_value': min_value,
    'max_value': max_value,
    'left_lon': left_lon,
    'right_lon': sright_lon,
    'lon_step': lon_step,
    'nx': nx,
    'top_lat': top_lat,
    'bottom_lat': bottom_lat,
    'lat_step': lat_step,
    'ny': ny
}
with open("{t.json"), "w") as f:
    json.dump(image_info, f, indent=2)

效果

一个温度场的示例,灰度图像:

描述文件:

{
  "min_value": 319.39718750000003,
  "max_value": 233.5571875,
  "left_lon": 0.0,
  "right_lon": 359.75,
  "lon_step": 0.25,
  "nx": 1440,
  "top_lat": 89.875,
  "bottom_lat": -89.875,
  "lat_step": 0.25,
  "ny": 720
}