资讯动态

grib2json:气象GRIB2数据高效转JSON实用指南

发布时间:2026/9/13 2:54:03 来源:尧图企业网站定制
简介grib2json 是一个基于 Java 的气象数据格式转换工具主要面向需要解析和交换气象数据的科研人员、数据分析师及 Web 开发者。grib 是世界气象组织制定的二进制网格格式常用来存储温度、湿度、风速、风向等预报变量但其二进制结构复杂在 Web 应用与数据分析流程中不易直接使用。该工具可将 grib 文件转换为开放的 JSON 格式从而便于可视化、统计分析和业务系统集成。资源包共 41 个文件容量仅 86KB包含 11 个 sample 示例数据、9 个 Java 源码文件以及 Markdown 文档、XML 配置、head、config 等辅助文件目录结构清晰适合学习 grib 解析原理也可直接嵌入到已有 Java 项目中复用。已有 396 人浏览学习是一份轻量且实用的气象数据预处理参考。通过包内示例与说明使用者能够快速理解 grib 字段到 JSON 的映射过程并针对温度、湿度等变量进行定制输出为后续的天气可视化或数据服务提供便利。1. grib2json 是气象数据进入 Web 体系的捷径拿到上游下发的 .grib2 文件时不少团队的默认路线是上 Python 的 cfgrib 或 xarray但在生产服务器上装依赖、调 engine、对齐版本常常比数据本身还费时间。grib2json 打开的路径更短它把 GRIB2 二进制里按模板组织的消息翻译成带 header、parameter、surface、data 等字段的 JSON后端拿到就能直接用。grib2 格式本身不是给人读的JSON 是这个工具的价值就是把前者结构化成后者。适合读这篇的人是要接气象数据做产品接口、写数据管线或者在纯 Java/JVM 环境里处理栅格数据的工程师读完能独立把转换服务搭起来。2. 看懂 grib 的 section 结构才能理解 grib2json 的映射逻辑2.1 GRIB2 消息本身就是一段段 sectionJSON 只是它的镜像GRIB2 文件可以拆成一连串消息message每条消息用 8 个数字编号的 section 组成。section 0 是指示段说明 GRIB2 版本和整条消息长度section 1 是标识段记录中心编号、子中心、参考时间section 2 是本地使用段通常被上游机构用来放自定义元数据真正影响解析结果的是 section 3 到 section 7。section 3 定义网格模板告诉解码器这是经纬度格还是高斯格、网格点数是多少section 4 是产品定义段核心字段包括 discipline、parameterCategory、parameterNumber 和层类型surface typesection 5 说明数据表示方式比如精度是 8 位还是 16 位section 6 是位图段用于标记哪些格点是缺测section 7 装的是压缩后的数据本体常见压缩算法有简单打包、PNG 和 JPEG2000 三种。所以想用正则或字符串切分去解析 GRIB2 是不可行的数据段的压缩方式不固定元数据的位置随模板变。grib2json 内部依赖 netCDF-Java 的 GRIB2 解码器由它把 section 3 的网格描述、section 4 的产品参数、section 5 与 section 6 的数据表示逐项解析成通用对象再转成 JSON。你看到的 parameter 字段基本来自 section 4grid 相关字段来自 section 3data 数组来自 section 7 解压后的值。理解这层对应关系后面排查“为什么这个字段没有”“为什么 data 长度不对”就有方向了。2.2 安装 grib2json 并先跑 --help确认参数再动手grib2json 是个 Java 命令行工具部署形态就是一个 jar 包加一套 lib 依赖。常见方式是下载 zip 或 tar 包解压目录里能看到可执行脚本或直接是 jar 文件。我不在这里贴具体下载链接你按“grib2json release”检索最新打包即可。解压后先用两条命令验证环境第一条查 Java 版本第二条看帮助。java -version java -jar grib2json.jar --help如果你解压后看到 bin 目录下有可执行脚本也可以直接用./bin/grib2json --help效果一样。执行后能看到工具支持的参数列表。不同发行版的参数命名有细微差别有的版本用短参数-d、-fc有的版本全部走长参数--data、--filter.category所以第一步务必看--help输出不要凭记忆写参数。注意grib2json 依赖 Java 8 以上运行时。如果你的服务器上默认 Java 版本太老先用update-alternatives切换版本或者直接用JAVA_HOME指定 JDK 路径否则启动时会在 class 版本校验处直接报错。2.3 第一个最小命令先输出元数据再带出数据数组拿到一个 GRIB2 文件后我一般先不加任何参数跑一遍看默认输出长什么样。java -jar grib2json.jar sample.grib2 meta.json这条命令默认只输出精简后的元数据不含 data 数组文件小、跑得快适合确认解码器有没有正确识别出变量。meta.json 里能看到每条消息对应的参数名称、单位、层次、参考时间这些信息。确认文件能解后再加参数输出数据。java -jar grib2json.jar -d -p sample.grib2 full.json-d表示输出数据数组是 data 字段的开关-p让 JSON 带缩进开发阶段方便人眼检查。生产环境我反而不建议加-p体积会大出三分之一。跑完后用 jq 快速看一眼总体结构jq .[0] | {parameter, surface, hasData: (.data ! null)} full.jsonjq 的.[0]取的是第一条消息。这个检查能同时确认三件事文件有没有被解码成多条记录、第一条记录是不是你想要的变量、data 字段是否真的被带出来。如果这里拿到的变量列表和预期不一致下一步就是过滤参数的事。3. 用 filter 参数精确切 layer避免输出体积失控3.1 category、value、surface 是 GRIB2 的产品编码不是文件路径GRIB2 的 section 4 里discipline 是最高层分类气象产品为 0水文为 1陆地产品为 2。discipline 下面是 parameter category温度类在 0湿度类在 1动量类在 2质量类在 3category 下面是 parameter number具体到某个变量。这些数字组合起来才是一个完整的变量标识。grib2json 的过滤参数照搬的是这套编码--filter.category对应 category--filter.value对应 parameter number--filter.surface对应层类型--filter.surface.value对应层数值。常用变量与编码组合可以记一张小表实际排错时拿它对照省很多时间。变量categoryvalue常见 surface 用法温度00103等压面搭配 850、500 等相对湿度11103 搭配 850、700 等风 U 分量22103 搭配 850 或 10高度层风 V 分量23103 搭配 850 或 10位势高度35103 搭配 500、700 等海平面气压31101海平面不需要额外数值注意这套编码定义在 WMO GRIB2 Code Table 4.1 和 4.2 里上游数据制作中心可能使用自定义子类。拿到陌生文件时不要硬套表后面会讲怎么从文件里直接反查编码。3.2 用 --filter.category 和 --filter.value 把单个变量剔出来假设文件里有很多变量只想要 850hPa 温度。常见写法是同时限制参数类别和层次java -jar grib2json.jar -d -p \ --filter.category 0 \ --filter.value 0 \ --filter.surface 103 \ --filter.surface.value 850 \ sample.grib2 t850.json逐个看参数含义--filter.category 0把范围收到温度类--filter.value 0指定编号 0也就是温度本身--filter.surface 103说明只要等压面层--filter.surface.value 850把层次钉死在 850hPa。四个条件组合起来输出里就只剩一条消息。这一步最常见的误用是只加--filter.category不加后面的 value。这样文件里有几条温度记录就会输出几条数据量没降下来。反过来--filter.surface不指定时温度在 500hPa、700hPa、850hPa 等每一层都会各出一条。判断过滤是否生效还是用 jq 统计输出条数正常情况应该为 1。jq . | length t850.json3.3 多记录文件用 -i 切数据集用 -v 反查变量编码有的 GRIB2 文件会打包多个时间步或几十个层次的数据。grib2json 默认拿第一条记录输出遇到想要后面某条记录时就需要-i参数指定记录序号。java -jar grib2json.jar -d -p -i 3 sample.grib2 record3.json-i的序号从 1 开始不是从 0。要确定第几条记录是要找的那个先用 verbose 模式把所有记录的头部信息打出来java -jar grib2json.jar -v -p sample.grib2 records.json然后jq .[] | {parameter, surface} records.json一页页看。每条记录的 parameter 对象里会带 category 和 numbersurface 对象里带 type 与 value这就是我要反查变量编码的方式比对着 WMO 码表猜可靠得多。4. 输出 JSON 里没有经纬度数组data 平铺后要手动重建坐标4.1 输出结构长什么样header、parameter、surface、forecastTime、data带-d输出的 JSON 是一个数组数组里每个元素代表一条 GRIB2 记录。单条记录常见结构如下{ header: { discipline: 0, disciplineName: Meteorological products, gribEdition: 2, center: 7, centerName: US National Weather Service, refTime: 2024-03-11T12:00:00.000Z, productDefinitionTemplate: 0 }, parameter: { category: 0, categoryName: Temperature, number: 0, numberName: Temperature, unit: K }, surface: { type: 103, value: 850, name: Isobaric surface }, forecastTime: { startTime: 1710158400000, timeZone: 0, endTime: 1710158400000 }, data: [263.4, 263.5, 263.6] }header 里能看到 GRIB2 标识信息parameter 描述变量身份surface 描述层次forecastTime 是预报参考时间。数据实体全在 data 数组里每个元素对应一个格点值顺序是定死的但不带任何坐标信息。这是 grib2json 设计上最需要适应的点JSON 只给了数值流没有给经纬度网格。4.2 data 数组按行主序展开从北到南一维数组要靠 nx/ny 还原GRIB2 数据在 section 3 里定义了网格点数通常记作 nx 和 ny但 grib2json 的 JSON 里不一定直接暴露这两个字段。常见做法是把 nx/ny 从上游数据集描述里另取一份比如配置文件或文件名规则再配合 latitude/longitude 的起止值完成坐标重建。取一个 0.25 度全球网格nx 是 1440ny 是 721。data 数组的输出顺序是逐行展开先是最北边一行从西到东全部格点再换到往南一行继续。把一维数组 reshape 成二维矩阵的 Python 代码很简单import json import numpy as np def load_grib2json(path, nx, ny): with open(path, encodingutf-8) as f: record json.load(f)[0] data np.array(record[data], dtypenp.float64) return data.reshape(ny, nx)reshape 前的关键检查是data.size nx * ny。如果两者不一致说明源文件带了位图某些格点是缺测值直接 reshape 会报错。处理这类文件时我一般先把缺测值填成 NaN再做维度变换raw np.array(record[data], dtypeobject) raw[raw None] np.nan # JSON 里的 null 先转成 NaN data raw.astype(np.float64).reshape(ny, nx)生成经纬度坐标时也有一点容易反纬度从北纬 90 度开始递减经度从西经 180 度开始递增。对应公式是先构造纬度向量和经度向量再用 numpy 的 broadcasting 生成网格。这一步做完才能把 data 里的二维矩阵和地图坐标对起来后续做等值线、风场箭头或者任意点插值都需要这个矩阵。4.3 三个常被读错的字段时间戳、surface.value、缺测forecastTime 里的 startTime 和 endTime 是毫秒级 Unix 时间戳不是 ISO 字符串。前端显示要除以 1000 再交给 Date 对象否则时间会大 1000 倍。这点在对接数据可视化组件时非常坑。surface.value 的含义依赖 surface.type。type 为 103 时 value 是百帕数850 就是 850hPatype 为 100 时 value 是气压差type 为 103 以外的层次比如 10 米高度风value 就是高度海拔数值。所以不要把每一层的 value 都当成 hPa先看 type 再解析数值。缺测值在 JSON 里一般表现为 null 或者 NaN。带进 JSON 时 null 更常见Python 里直接np.array会变成 object 类型要先用上面那段代码替换再转 float。不处理的话后续所有数值计算都会带上脏数据。层值里偶尔也会混进 9999 这种哨兵值这种是上游制作时就写死的遇到再单独清洗。5. 批量目录转换时把参数封装成函数过滤、降体积一步到位5.1 写一个 shell 函数把 grib2json 参数模板固定下来命令行工具容易踩的坑是参数一长就容易写错或不同人写的参数不一致。我在项目里习惯抽一个 shell 函数把固定参数收进去。下面这个例子把 category、value、surface 和输出路径都参数化grib2json_slim() { local input$1 local category$2 local value$3 local surface_type$4 local surface_value$5 java -jar /opt/grib2json/grib2json.jar -d -c \ --filter.category $category \ --filter.value $value \ --filter.surface $surface_type \ --filter.surface.value $surface_value \ $input }注意这里用-c而不是开发时用的-p生产环境输出不缩进体积更小。函数接收五个参数前四个决定取哪个变量第五个是输入文件。调用示例grib2json_slim /data/raw/20240311.grib2 0 0 103 850 /data/json/20240311_t850.json上面的调用取出 850hPa 温度写到一个独立 JSON 文件里适合后续按变量分目录管理。参数改成2 3 103 850就能换风场 V 分量函数的复用价值比一条写死的命令高。5.2 目录循环转出全部文件的注意点一个批次的 GRIB2 文件往往按小时落盘用 for 循环逐个过一遍是常规操作。文件名里的时间和变量信息要保留在输出名里否则批次多了找不到对应关系。for f in /data/raw/20240311/*.grib2; do base$(basename $f .grib2) grib2json_slim $f 0 0 103 850 /data/json/${base}_t850.json grib2json_slim $f 2 2 103 850 /data/json/${base}_u10.json done这个循环里有两个地方值得留意basename把 .grib2 后缀剥掉输出时再拼上新后缀变量名如 t850、u10 直接嵌进文件名后续检索时按前缀就能筛出来。如果文件多达几百个建议加一层xargs -P 4做并发转换避免单线程等待太久。并发场景下 JVM 内存要提前调默认堆太小会出现 OutOfMemoryError。常见做法是在启动前显式指定堆大小export JAVA_OPTS-Xms512m -Xmx2g如果转换中途挂掉优先查两处一是日志里的记录序号确认是不是某条记录数据异常二是输出文件体积体积为零基本就是 filter 参数组合没匹配到任何记录。5.3 批量完成后做一次体积校验确认过滤真正生效转完一批我会跑一条命令看输出文件大小列表和源 GRIB2 的体积做对比。如果 filter 没生效JSON 会是源文件的数倍大很容易在 ls 输出里露馅。ls -lh /data/json/20240311_*_t850.json | awk {print $5, $9}预期单时次的 850hPa 温度 JSON 应该在几 MB 到几十 MB 量级取决于网格分辨率。如果某文件明显偏大用 jq 查一下记录数确认是不是混入了多层数据。这一轮校验做完转换管线的输出就是稳定、可预期、能被下游 JavaScript 或 Python 服务直接消费的状态。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价