雨量等值线是防汛抗旱指挥系统中的核心可视化成果,它能将全省乃至全国范围内离散的雨量站点观测数据,转换成一张连续的降雨分布图,帮助指挥人员快速识别强降雨中心、评估洪涝风险区域。传统的做法是在服务端生成图片再推给前端,但这种方式交互性差、无法动态高亮某个等值区间,也难以支撑数据的分钟级刷新。本文将以jQuery为主要技术栈,完整讲解如何在浏览器端动态生成并渲染雨量等值线,覆盖数据插值、等值线追踪、图层渲染与实时更新四个关键环节。

一、雨量站点数据的组织与插值算法选型
雨量站上报的数据本质上是离散点集合,每个点包含经度、纬度和时段降雨量三个属性。等值线生成的第一步,就是把离散点插值成规则的栅格矩阵,否则无法进行等值线追踪。前端可选的插值算法主要有三种:最近邻插值、反距离权重(IDW)和克里金(Kriging)。最近邻计算量最小但效果粗糙,会出现明显的泰森多边形斑块;IDW实现简单、效果适中,适合站点较密的中等尺度区域;Kriging考虑了空间自相关性,生成的曲面平滑且更符合水文规律,是防汛系统的主流选择,缺点是半变异函数拟合有一定计算量。
对于省市级防汛指挥系统,站点数量通常在几百到几千个之间,Kriging在浏览器端完全跑得动。推荐使用开源的kriging.js库,它只有几百行代码,可以直接引入页面,也可以改造后配合jQuery的$.ajax使用。下面是数据准备阶段的标准结构:
// 站点原始数据,通常由后端接口返回
var stationData = {
coordinates: [
[118.78, 32.04], [118.86, 32.11], [119.02, 31.95]
],
rainfall: [45.2, 78.6, 12.3] // 对应站点的时段雨量,单位mm
};
// 后端接口约定:返回最新一批站点雨量
function fetchRainfall(callback) {
$.ajax({
url: '/api/rainfall/latest',
type: 'GET',
dataType: 'json',
success: function (resp) {
if (resp.code === 0) {
callback(resp.data);
}
},
error: function () {
console.warn('雨量数据拉取失败,沿用上一批数据');
}
});
}
需要特别注意的是,经纬度不能直接拿去插值。经度要乘以纬度余弦进行等距校正,否则在高纬度地区横向距离会被夸大,生成的等值线形状会失真。校正逻辑很简单,在插值前对坐标做一次预处理即可。
二、Kriging插值栅格化与等值线追踪的前端实现
拿到离散点后,先用Kriging训练模型,再在目标区域内按固定步长采样,生成栅格矩阵。栅格分辨率决定了等值线的精细程度:步长取区域跨度的百分之一左右,视觉效果和性能比较均衡,太细会导致计算耗时明显增加,在低配机上会卡顿。等值线的分级标准应遵循水文行业的规范,常用的分级序列为1、5、10、25、50、100毫米,可结合本地暴雨特点调整。
栅格化完成后,使用Marching Squares算法逐级追踪等值线。kriging.js本身提供了一个getContours工具,可以直接输出各个级别的多边形坐标串,省去手写追踪算法的工作量。下面是完整的插值与等值线提取流程:
// 1. 训练克里金模型(指数模型,sigma根据站点分布尺度调整)
var variogram = kriging.train(
stationData.rainfall,
stationData.coordinates,
'exponential', 0, 100
);
// 2. 定义绘图范围(经纬度边界框)与栅格精度
var extent = [117.0, 30.5, 121.5, 33.5];
var gridWidth = 360; // 栅格列数
var gridHeight = 300; // 栅格行数
// 3. 栅格化并获取等值面多边形
var polygons = kriging.getContours(
variogram,
[1, 5, 10, 25, 50, 100], // 等值线分级,单位mm
extent,
gridWidth,
gridHeight
);
// 4. 交给渲染模块
renderRainfallLayer(polygons);
这里有一个容易踩的坑:站点数据里偶尔会出现缺测值或异常值(比如负数、超过历史极值的数字),如果不做清洗直接插值,整个曲面会被单个坏点拉偏,等值线会出现不合理的闭合圈。建议在调用插值前增加一道数据校验,剔除明显离群的观测值,并对返回的坐标做范围合法性检查。
三、用jQuery组织实时更新与动态重绘
防汛场景下雨量数据通常每5到10分钟更新一次,汛期加密到每分钟一次。前端的更新机制有两种:定时轮询和WebSocket推送。轮询实现简单,用setInterval配合$.ajax即可,缺点是存在延迟且有无谓的空请求;WebSocket实时性好,但需要后端改造。折中方案是采用轮询加服务端版本号比对,只在数据变化时返回全量数据,否则返回304式的轻量响应。
// 方案一:定时轮询 + 版本号增量判断
var lastVersion = '';
function startPolling(intervalMs) {
setInterval(function () {
$.ajax({
url: '/api/rainfall/latest',
data: { version: lastVersion },
dataType: 'json',
success: function (resp) {
if (resp.code === 0 && resp.version !== lastVersion) {
lastVersion = resp.version;
var variogram = kriging.train(
resp.data.rainfall,
resp.data.coordinates,
'exponential', 0, 100
);
var polygons = kriging.getContours(
variogram, [1, 5, 10, 25, 50, 100],
extent, gridWidth, gridHeight
);
renderRainfallLayer(polygons); // 增量重绘
}
}
});
}, intervalMs);
}
重绘时要避免整页刷新的观感问题。推荐的做法是准备两层canvas,一层作为缓冲层离屏绘制新图,绘制完成后一次性交换到显示层,用户看到的是平滑过渡而非白屏闪烁。同时可以用jQuery的$.Deferred管理插值、追踪、渲染三个异步步骤,保证任务队列不乱序。如果页面还叠加了行政区划裁剪需求,可以让后端返回边界GeoJSON,前端在canvas上用clip路径把等值面裁剪在行政区范围内,这样图面干净且符合指挥大屏的审美要求。
四、Canvas与SVG渲染方式对比及落地建议
等值面的渲染载体选canvas还是SVG,直接决定系统的性能上限。两者的核心差异如下表所示:
| 对比维度 | Canvas渲染 | SVG渲染 |
|---|---|---|
| 几千个多边形的表现 | 绘制一次后性能稳定,适合高频刷新 | DOM节点多,重绘时卡顿明显 |
| 交互能力 | 需要自己做坐标命中检测 | 天然支持事件绑定与hover高亮 |
| 导出与打印 | 可直接toDataURL导出图片 | 需借助其他库转换 |
| 适用端 | 指挥大屏、桌面端 | 移动端、交互密集页面 |
对于防汛指挥大屏,强烈建议用canvas:等值面更新频率高、多边形数量大,canvas一次draw调用就能完成整层绘制,配合requestAnimationFrame还能做颜色分级的渐变动画,让新一小时的等值面平滑过渡到旧图之上。而对于移动端巡查App内嵌页,SVG的优势更明显,每个等值面多边形都是一个独立元素,可以直接用jQuery绑定click事件弹出该区间的面积统计与站点明细,代码非常简洁:
// SVG模式下为每个等值面绑定点击事件
$('#rainfall-svg').on('click', '.contour-polygon', function () {
var level = $(this).data('level');
var area = $(this).data('area');
showDetailPanel('雨量区间 ' + level + ' mm,覆盖面积约 ' + area + ' 平方公里');
});
最后再给几条工程化建议:插值计算如果站点规模超过五千个,考虑把栅格化环节下沉到服务端用GDAL完成,前端只负责渲染坐标串;颜色分级方案要参照水利行业标准的雨量色带,从浅蓝到深红逐级加深,并保证色盲用户也能区分;所有异步环节统一加超时与降级处理,数据拉取失败时保留上一次的等值线并给出提示,避免大屏出现空白图层。按照这套方案落地,一个基于jQuery的雨量等值线模块可以在绝大多数现有防汛系统中快速集成,无需引入重型前端框架,改造成本低且效果稳定。