简介:面向需要从中国地面气候资料日值数据集(V3.0)中批量提取气温、降水要素的科研人员与气象爱好者,这份工具包提供了一体化处理方案:C#编写的处理软件可快速将日值数据转换为月/年平均或总量,并支持选择输出类型。软件基于VS2019开发,压缩包内含可直接运行的exe、完整源码及工程配置(.sln/.csproj/config/json),开发者可自由修改算法或扩展其他气象要素;同时附带的全国气象站点矢量数据覆盖全部站点,提供shp、shx、dbf、prj等标准GIS格式,便于在ArcGIS/QGIS中直接加载、配准和空间分析。包内使用说明书(docx)和文本说明详细讲解了输入路径的配置方式,默认对应C:\Users\Lenovo\Downloads\v3.0下的两个年份文件夹,可大幅降低上手门槛。整个资源共41个文件,仅239KB,轻巧紧凑,已有1055人学习下载。无论是做气候统计、地图制图还是教学演示,这套工具都能省去繁琐的数据清洗环节,直接产出可用的研究成果,兼具实用性与可维护性。
1. 从拿到压缩包到跑出第一份 CSV:为什么 VS2019 和站点矢量数据会卡住绝大多数人
搞气象评估、农业区划或者写论文的人,下载到“中国地面气候资料日值数据集(V3.0)处理软件”之后通常会被两步卡死:第一步是软件只给工程源码,要求至少 VS2019 才能编译,很多人装的是 VS2022 或者干脆没装 C++ 桌面组件;第二步是终于跑通后,发现输出的结果表和“全国气象站点矢量数据”对不上号——站号前导零丢了、经纬度差出几公里、站名字段乱码。这篇笔记按一次完整落地的顺序来:先讲清 V3.0 日值数据和站点 shp 的结构关系,再给 VS2019 下编译并运行处理软件的最小操作,然后把 CSV 和全国气象站点矢量数据做空间关联,最后整理五条踩坑记录和一份按月批处理的进阶方案。适合预报员、GIS 工程师和需要自行处理气象数据的研究生参考。
2. V3.0 日值数据集的结构:先弄清楚 TXT 里的“压缩整数”与站点矢量数据怎么互认
2.1 每个日值 TXT 里到底装着什么:要素列、编码单位与质控标志位的关系
中国地面气候资料日值数据集(V3.0)对外分发时并不是给你一份干净的 Excel 表,而是按站点和要素打包好的文本文件。最常见的组织方式是:一个站点一个文件,或者一个省区多个站点合并到一个文件;文件名前缀形如 SURF_CLI_CHN_MUL_DAY,后面跟着区域号、年月信息,偶尔也有按要素单独拆分的文件。这些文件的正文每一行代表一个站某一天的观测记录,列上依次是区站号、年、月、日,然后是“要素值 + 质控标志位”的交替序列。有的文件只放单要素,有的把气温、降水、风速、日照等多个要素交错排列,处理软件就是要按固定的列顺序把它们切开并重组。
区站号是这批数据最关键的主键。气象台站在全国站网里的编号是五位数,例如南京的 58238、上海的 58362;在“全国气象站点矢量数据”的 shp 属性表里,也维护着同一个五位区站号。只要这两个主键能稳定对上,后续空间分析才有意义。很多新人没有意识到:原始 TXT 里的区站号是以文本形式存储的,部分不规范的导出会把 00582 这种带前导零的站号写成 582,Excel 打开时进一步把前导零抹掉,导致与 shp 匹配时整表横断。这个问题我后面专门有一节来展开解决。
处理软件做的事可以拆成两件。第一,把 V3.0 文本里以整数编码保存的气象值还原成真实观测值。本数据集大部分要素采用 0.1 倍率编码,例如原始值 37 表示 3.7 摄氏度,108 表示 10.8 毫米降水量,2147 表示 214.7 hPa 平均气压。不同要素的倍率稍有差别,不能一概而论,见下表。
| 要素 | 变量名 | 编码倍率 | 解码后单位 |
|---|---|---|---|
| 平均气温 | TEM | ×0.1 | ℃ |
| 最高气温 | MAX | ×0.1 | ℃ |
| 最低气温 | MIN | ×0.1 | ℃ |
| 降水量 | PRE | ×0.1 | mm |
| 平均风速 | WIN | ×0.1 | m/s |
| 日照时数 | SSD | ×0.1 | h |
| 平均气压 | PRS | ×0.1 | hPa |
第二件事是切分数值与其紧随其后的质控码。质控标志位是本数据集区别于 V2.0 的核心之一:0 表示数据通过质检、1 表示可疑、2 表示错误或已经过修正。如果处理软件在导出 CSV 时把质控码一并写出,后续统计时还能补救;如果直接丢弃,你面对的就是一份无法辨别真假的时间序列。个人建议无论做气候平均还是极端事件分析,一律保留质控码列,导出后再按业务需要筛选。
2.2 台站参数文件、月值文件与站点 shp:三套文件里的经纬度和海拔为什么对不上
和日值数据一起下载的通常还有一份台站参数文件,文件里一行一个站,字段按“区站号、站名、省份、纬度、经度、观测场海拔、气压传感器海拔”排列,其中经纬度大多以十进制度存储。全国气象站点矢量数据的属性表里同样有站号、站名、经纬度、海拔字段,而且因为来源不同,两套坐标会出现小数点后第四位不一致的情况,这在几公里尺度上是能察觉的。我的经验是:空间位置一律以 shp 属性表为准,日值数据里的经纬度只用于抽样核对,不参与地图制图。
坐标还有一个容易被忽略的单位差异。部分台站参数文件是从老数据库转换来的,经纬度列的历史版本曾使用“度分”混合格式,或者“整数度 × 100”的压缩格式。比如 116.28°N 可能被存成 11628,如果不经过处理软件换算直接当十进制度用,站点就会离真实位置几百公里。处理软件在输出阶段会把统一格式的十进制度写进结果,我建议拿到结果后先抽查上海、北京、广州三个知名站点,确认纬度落在预期范围内再继续。
海拔字段同样有坑。观测场海拔表示气象站地坪高度,气压传感器海拔表示测量气压表所在的高度,二者差几米到几十米不等。做地面温度分析时用哪个影响不大,但做气压订正或者高空观测对比时,取错列会导致系统性偏差。全国气象站点矢量数据里通常只保留一个海拔值,需要和台站参数文件做逐站比对后才能混用。
编码问题也是三套文件互认时的重灾区。shp 属性表的 DBF 部分默认采用 GBK 或 GB2312 存储中文站名,而处理软件输出的 CSV 往往按 UTF-8 编码;在 QGIS 里两者分开打开都正常,一旦用 Python 的 pandas 合并,一边按默认编码读、一边按 gbk 读,就会出现乱码。我统一的做法是:处理软件输出 UTF-8 带 BOM 的 CSV,python 读 CSV 时用 encoding="utf-8-sig",读 shp 时显式传入 encoding="gbk",两边站名才能都对上。
3. 用 VS2019 编译并运行处理软件:从源码到跑通第一个日值 TXT 文件
3.1 安装 VS2019 与打开工程:v142 工具集缺失是最常见的编译失败原因
标题里特意写“需要至少配备 VS2019”,潜台词是这批源码里用到了较新的 C++ 标准语法,VS2017 的 v141 工具集很可能编译不过。安装 VS2019 时有个容易踩空的环节:默认安装不含“使用 C++ 的桌面开发”工作负载,你要在 Visual Studio Installer 里手动勾选它,同时选上 Windows 10 SDK 和 MSVC v142 构建工具。如果之前只装过 VS2022,也别急着卸,先尝试用 VS2019 打开 .sln,在解决方案资源管理器里右键工程,进入“属性 → 常规 → 平台工具集”,把它改回 Visual Studio 2019 (v142)。
命令行编译是我一贯推荐的方式,因为输出信息比 IDE 完整,报错定位也直接。先调用 VS2019 自带的开发环境初始化脚本,再执行 msbuild:
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\Tools\VsDevCmd.bat" -arch=x64 msbuild 日值V3.0处理软件.sln /p:Configuration=Release /p:Platform=x64 /m第一行命令把编译所需的 cl.exe、link.exe 和 include 路径全部注入当前终端,-arch=x64 指定生成 64 位程序;如果你装的 VS2019 是 Professional 或 Enterprise,路径里的 Community 要替换成对应目录名。第二行的 /m 参数表示多进程并行编译,能显著缩短时间;/p:Configuration=Release 和 /p:Platform=x64 必须与后续运行版本保持一致,避免出现 Debug 版编译、Release 版运行的混乱情况。如果 msbuild 报错“MSB8020 找不到 v142 生成工具”,多数原因是这台机器只装了 VS2022,需要在 IDE 中手动切换工具集后再试。
编译生成的 exe 通常在 x64\Release 目录下。这里有个提醒:exe 的动态链接依赖 VC++ 运行库,换一台机器运行时需要安装 vc_redist.x64.exe,否则会报“缺少 VCRUNTIME140.dll”。把工程属性里“C/C++ → 代码生成 → 运行库”改成“多线程(/MT)”可以做到静态链接,把运行库打进展 dir 里,部署起来省心很多,但要求所有代码模块没有 MFC 依赖,否则依然要带 MFC 动态库。
3.2 最小可运行配置:用配置文件把原始目录、站点参数、输出规则一次性说清楚
很多处理软件版本要求输入一个配置文件来指定工作参数。这是我常用的一个最小配置模板:
[输入] ; 原始日值 TXT 文件所在目录,注意不要有中文路径 原始目录=D:\ClimateData\SURF_CLI_CHN_MUL_DAY ; 台站参数文件路径,用于输出经纬度和海拔 站参数文件=D:\ClimateData\台站参数.txt [输出] ; 转换结果的 CSV 输出目录 输出目录=E:\Processed ; 1 表示剔除质控码为 2 的错误数据,0 表示全部保留 剔除错误=1 ; 输出站号补零位数,5 表示统一保留 5 位站号 站号补零=5 ; 输出编码,0 为 UTF-8,1 为 GBK 输出编码=0这段配置的逻辑并不复杂:程序启动后先读 [输入] 段定位原始数据和台站参数,然后遍历原始目录里的 TXT,把每个站逐日记录按 0.1 倍率还原,再按 [输出] 段的规则裁剪质控码和补零。关键参数是“剔除错误”和“站号补零”。“剔除错误”这个开关要看你做什么:做长期气候平均时,我一般开启,因为质控码 2 的数据已经是确证错误;但分析台风、寒潮等极端过程时,有时可疑数据反而记录了真实极端天气,我会关闭剔除开关,把质控码列导出后人工核查。“输出编码”选 UTF-8 是给 Python 用的,选 GBK 是给 ArcGIS 的属性连接直接读取用的,两者不要混。
程序跑完后,输出目录里会多出若干 CSV 文件,命名格式类似 58238_2020_TEM.csv,代表 58238 站 2020 年平均气温逐日序列。如果你下载的是多要素混合文件,输出可能是 58238_2020.csv,每个文件的列里同时包含 PRES、TEM、RHU、PRE 等字段,筛选列时注意区分。第一次跑完不要急着做统计,先打开 CSV 抽查第一年和最后一年的数据行,对照原始 TXT 里同一日期、同一要素的编码值,验证倍率换算有没有出问题。
4. 让处理结果和“全国气象站点矢量数据”对得上:属性关联、投影检查与 WKT 导出
4.1 基于区站号的左关联:pandas 和 geopandas 的组合操作
处理软件产出的 CSV 通常是纯属性表,没有几何信息;全国气象站点矢量数据则是带经纬度的面或点图层。把两者合成一份可用于绘图或空间统计的数据时,标准动作是以区站号为主键做左关联。用站名关联要尽量避免,全国站名有重名现象,缩写规则也不统一,五位数站号才是唯一主键。
写一段可以直接跑的 Python,内置了对前导零和编码的防御:
import pandas as pd import geopandas as gpd df = pd.read_csv("58238_2020_TEM.csv", dtype={"站号": str}) df["站号"] = df["站号"].str.zfill(5) gdf = gpd.read_file("全国气象站点矢量数据.shp", encoding="gbk") gdf["站号"] = gdf["站号"].astype(str).str.zfill(5) merged = gdf.merge(df, on="站号", how="left") print(merged.crs) print(merged.head())这段代码的要点是:read_csv 用 dtype 显式把站号读成字符串而不是数字,防止 58238 这类站号被 read_csv 自动转成 int64 丢掉;str.zfill(5) 会把“58238”左侧补零到 5 位,即使原始站号在某个环节被写成“8238”也能恢复;gdf 读取时指定 encoding="gbk",避免中文站名乱码。merge 完成后的 merged 是 GeoDataFrame,可以继续做空间分析。
注意 geopandas 依赖 GDAL 环境,建议通过 conda 安装 geopandas,而不是手动 pip 装底层库。如果你的分析不需要空间操作,只想把 CSV 关联到站点的经纬度坐标,也可以用普通 pandas 读取两个 CSV 后 merge,稍微快一些。
如果日值数据量很大,比如你要关联近 30 年全国两千多个站的逐日数据,merge 结果会有几百万行,在 GIS 里做属性连接必然卡死。我会先把日值按月聚合为月平均或月累计,再和 shp 关联;要是仍然太大,就改用数据库,把 CSV 灌进 SQLite 或 PostgreSQL,用结构化查询完成关联,而不是在内存里死磕。
4.2 空间自检三步走:用抽查、距离计算和投影统一来排查错位
关联只是第一步,位置对不对需要验证。我总结了三步自检法。
第一步抽查知名站点。依次打印北京(54511)、上海(58362)、拉萨(55591)三个站点的经纬度,和手头已知值比对,误差超过 0.02 度就要警惕。
第二步计算距离。在 geopandas 里先用样条函数把两套坐标系投影到同一基准,然后计算 CSV 里的经纬度到 shp 点位的距离:
import pandas as pd import geopandas as gpd from shapely.geometry import Point gdf_xy = gpd.GeoDataFrame( merged, geometry=[Point(x, y) for x, y in zip(merged["原始经度"], merged["原始纬度"])], crs="EPSG:4326", ) gdf_xy = gdf_xy.to_crs("EPSG:32650") gdf_base = gdf.copy() gdf_base = gdf_base.to_crs("EPSG:32650") merged["偏移距离_m"] = gdf_xy.geometry.distance(gdf_base.geometry) print(merged[["站号", "偏移距离_m"]].describe())这里把两套经纬度都先统一到 WGS84 的 EPSG:4326 再投影到 UTM 50N(EPSG:32650)计算平面距离。如果全国绝大多数站点偏移在 100 米以内,说明 CSV 经纬度与 shp 一致;如果出现几千到上百公里的偏移,通常是投影单位没换算或者 CSV 里留存了“度分”格式的经纬度,需要回到第 2 章的倍率检查。
第三步处理投影不一致。当 shp 的 crs 是 CGCS2000,而 CSV 的经纬度基于 WGS84 时,在 QGIS 里开启 OTF 实时投影看不出问题,但导出到某些离线服务就会错位。做法是给 gdf 指定正确的初始 crs,再用 to_crs 转成目标坐标。
有把数据送给第三方做 Web 可视化需求的朋友,顺带可以把几何转成 WKT 字符串,避免对方手边没有 shp 文件:merged 里的 geometry 列直接调 to_wkt() 导出即可,一行代码就能拿到经纬度、站名、要素值全部躺在同一张 CSV 里的结果。
5. 避坑:处理软件与站点矢量数据最容易翻车的 5 个细节
5.1 软件运行层面的坑:运行库、中文路径与前导零丢失
现象:VS2019 编译成功后,把 exe 复制到另一台电脑双击,弹出“找不到 VCRUNTIME140.dll”或“MSVCP140.dll”。
原因:exe 动态链接了 VC++ 运行库,目标机器没有装对应版本的运行库;还有一种情况是编译时选了 Debug 配置,Debug 运行库默认不随系统分发。
解决:统一编译成 Release x64,并在目标机器安装 vc_redist.x64.exe。如果要长期在多台机器部署,在工程属性里把运行库设为“多线程(/MT)”,让运行库静态链接进 exe,部署时不再依赖系统运行库。
现象:软件能编译,但运行时读不到数据文件,程序退出码是 0,输出目录里没有任何 CSV。
原因:数据路径里带了中文,比如“D:\气象数据\”,而处理软件内部采用 ANSI 代码页的文件操作函数读取路径,在某些 Windows 区域设置下无法识别中文路径。这种现象非常隐秘,往往和你系统区域切换有关,属于最调皮的一类玄学问题。
解决:把原始数据、输出目录一律放到纯英文字符路径下,例如 D:\ClimateData。宁可让自己的目录名丑一点,也绝不给编码问题留后门。
现象:处理软件输出的 CSV 用 Excel 打开,区站号 00582 变成了 582,所有前导零被静默删除。
原因:Excel 在打开无 BOM 的 CSV 时自动把纯数字列转为数值型,前导零被当作无意义字符丢弃。这不是处理软件的 bug,而是 Excel 自身的格式猜测行为。
解决:在输出阶段把区站号写成等长补零文本,或在 CSV 里用带引号的方式包裹站号字段;读取时在 pandas 里用 dtype=str 强制读为字符串,再做 zfill(5);如果你只在 Excel 里用,选中该列后设置“文本”格式再导入即可。
5.2 数据关联层面的坑:站名字段重名、质控码位偏移和经纬度误差
现象:用 pandas merge 按站号关联 CSV 和 shp 后,整列全是 NaN,一条有效记录都没剩下。
原因:两边站号字段的类型或字段名不一致。shp 属性表里的字段可能叫 Sta_ID,CSV 里叫 Station_Id,或者一边是文本一边是 int64,merge 时主键匹配不上。
解决:在 merge 前先打印 df.columns 和 gdf.columns,手动统一成同一个字段名;再用 df["站号"] = df["站号"].astype(str).str.zfill(5) 把两边都规范成五位数文本,最后才执行 merge。
现象:处理软件按配置剔除质控码为 2 的记录后,输出结果里仍然出现 -99.9 这类明显的缺失值占位符。
原因:部分老版本 TXT 中,缺测值不是“无数据”而是用 -99.9、-9999 之类的特殊编码填充,质控码本身可能是 0,因此“剔除错误”规则管不到它。
解决:处理软件输出后,再用一段清洗脚本把 -99.9、-9999、32766 这类极端占位符统一替换为空值,并生成一列记录替换位置的标记列。不要直接删行,因为这类记录还保留了日期信息,删行会破坏时间序列的连续性。
现象:把 CSV 和 shp 叠到地图上,发现站点整体向东南方向偏移约几百米到几十公里,但各站相对位置形状没变。
原因:shp 的坐标系是 GCS_CGCS2000,而 CSV 经纬度按 WGS84 存储,两者虽然很接近却不完全重合;如果偏移达到上千公里级别,则是 CSV 经纬度未做“度/分”换算,直接把“11628”当成了 116.28°。
解决:统一用 geopandas 先将 gdf 设置为其原始 crs,再 to_crs 到目标坐标系;对于不合理的经纬度数值,用第 2 节的知名站点抽查法快速定位。
现象:用站名做关联统计时,发现有的省站名完全不匹配,明明看到的是同一个站。
原因:站名表里存在旧名、新名和别名。例如一些站点在 2005 年后更名,shp 属性表更新滞后,导致按站名 join 失败。
解决:一律不用站名做主键,改用区站号。已经用站名 joining 过又出错的,可以回到原始台站参数文件,用旧站号反查新站名后再修正。
6. 把逐站日值快速聚合成站点月值表:一个可以反复用的落地框架
当数据源有几百个站、三十年以上逐日记录时,逐个点击处理软件图形界面是不现实的。我通常第一层交给处理软件做全目录批量转换,第二层用 Python 把输出 CSV 批量读入并按站号、年、月聚合。下面这段代码把多年逐日温度、降水量转换成站点月值表,直接可以和全国气象站点矢量数据 merge 出“月尺度站点空间表”:
import pandas as pd import glob csv_list = glob.glob(r"E:\Processed\*.csv") frames = [] for f in csv_list: tmp = pd.read_csv(f, dtype={"站号": str}, encoding="utf-8-sig") frames.append(tmp) df = pd.concat(frames, ignore_index=True) df["站号"] = df["站号"].str.zfill(5) df["年"] = df["日期"].str[:4].astype(int) df["月"] = df["日期"].str[5:7].astype(int) df["均温"] = pd.to_numeric(df["平均气温"], errors="coerce") df["降水"] = pd.to_numeric(df["降水量"], errors="coerce") month_table = df.groupby(["站号", "年", "月"]).agg( 月平均气温=("均温", "mean"), 月降水量=("降水", "sum"), 有效天数=("均温", "count"), ).reset_index() month_table.to_csv("站点_月值.csv", index=False, encoding="utf-8-sig")这里的操作逻辑是:glob 把输出目录中所有站的 CSV 都读进来,concat 成一张全量长表,然后按站号与年月分组。均值与求和分别是温度类、降水类要素的典型聚合方式;有效天数这个统计项很值得保留,它让你知道某个月份该站是否缺测超过阈值,避免用一个月只观测几天的不完整资料去做气候统计。
验证方法我有两个固定动作。第一个是取一个气候极稳的站,比如用北京站把处理后的 1 月平均气温序列画成折线,和公开的气候标准值(1981—2010 年平均)做对照,偏差通常应小于 0.3℃;如果出现整年整月的大幅偏差,方向对但整体系统性高或低,多半是倍率或质控规则设错了。第二个是随机抽三个站的手工抽查,打开原始 TXT 核对某一日的温度编码值和 CSV 输出值是否一致。能通过这两项验证,这批数据才能进入业务分析。
我自己的血泪经验是,处理这类数据永远不要相信“第一次就对了”,每次都从一小批测试文件开始跑,确认清晰后再放全量数据。挂上自动批处理之后也不能干等,把质控码列和有效天数列一直留在表里,做到什么时候都能追溯数据质量。希望这个从 VS2019 编译、日值转换、站点 shp 关联到月值聚合的完整流程,能帮你在下一个研究季少走几趟弯路。
本文还有配套的精品资源,点击获取