空气质量 WebGIS 可视化实战
背景与需求
近期湖南省出现了一次明显的空气污染过程,以长沙及周边地区为主。为了更直观地展示全省空气质量状况,我们尝试将百度天气接口数据与地理信息系统(WebGIS)结合,构建一个动态交互的展示平台。
除了查看表格数据,通过地图空间化展示能更清晰地识别污染范围,未来还可结合风场数据预判扩散趋势。
技术实现
1. 空气质量等级标准
首先明确 AQI 指数与颜色的对应关系,这是前端可视化的基础:
| 空气指数 | 空气状况 | 对健康影响情况 | 建议采取的措施 |
|---|---|---|---|
| 0-50 | 优 | 空气质量令人满意,基本无空气污染,对健康没有危害 | 各类人群可多参加户外活动,多呼吸一下清新的空气。 |
| 51-100 | 良好 | 除少数对某些污染物特别敏感的人群外,不会对人体健康产生危害 | 除少数对某些污染物特别容易过敏的人群外,其他人群可以正常进行室外活动。 |
| 101-150 | 轻度污染 | 敏感人群症状会有轻度加剧,对健康人群没有明显影响 | 儿童、老年人及心脏病、呼吸系统疾病患者应尽量减少体力消耗大的户外活动。 |
| 151-200 | 中度污染 | 敏感人群症状进一步加剧,可能对健康人群的心脏、呼吸系统有影响 | 儿童、老年人及心脏病、呼吸系统疾病患者应尽量减少外出,停留在室内,一般人群应适量减少户外运动。 |
| 201-300 | 重度污染 | 空气状况很差,会对每个人的健康都产生比较严重的危害 | 儿童、老年人及心脏病、肺病患者应停留在室内,停止户外运动,一般人群尽量减少户外运动。 |
| >300 | 严重污染 | 空气状况极差,所有人的健康都会受到严重危害 | 儿童、老年人和病人应停留在室内,避免体力消耗,除有特殊需要的人群外,一般人群尽量不要停留在室外。 |
2. 后端数据查询
在 PostgreSQL 数据库中,利用 PostGIS 扩展处理空间数据。我们需要关联天气表、行政区划表和地理名称表,提取几何信息并转换为 GeoJSON 格式供前端使用。
SELECT t2.*, T.province_code, T.province_name, T.city_code, T.city_name, T.area_code, T.area_name,
t1.geom, st_asgeojson(T.geom) geomJson, st_x(t1.geom) lon, st_y(t1.geom) lat
FROM biz_weather_now t2, biz_area T, biz_geographic_name t1
WHERE to_char(t2.uptime, 'YYYY-MM-DD') = '2025-10-12'
AND T.province_code = '430000'
AND T.area_name = t1.NAME
AND T.area_code = t2.location_code
AND st_contains(T.geom, t1.geom)
ORDER BY T.area_code;
执行后返回的结果包含各行政区的 AQI 值及对应的几何坐标,这是前端渲染的基础。
3. 前端集成 Leaflet
使用 Leaflet 库进行地图渲染,核心在于颜色映射与图例切换。
定义颜色配置
首先在前端定义 AQI 指数与颜色的映射关系,方便后续逻辑调用:
// AQI 空气指数配置
var aqiColorList = [
{name: "优 (0–50)", color: "#00FF00", rgb: new Color(0, 255, 0), colorDesc: "绿色"},
{name: "良 (51–100)", color: "#FFFF00", rgb: new Color(255, 255, 0), colorDesc: "黄色"},
{name: "轻度污染 (101–150)", color: "#FFA500", rgb: new Color(255, 165, 0), colorDesc: "橙色"},
{name: "中度污染 (151–200)", color: "#FF0000", rgb: new Color(255, 0, 0), colorDesc: "红色"},
{name: "重度污染 (201–300)", color: "#800080", rgb: new Color(128, 0, 128), colorDesc: "紫色"},
{name: "严重污染 (>300)", color: "#993366", rgb: new Color(153, 51, 102), colorDesc: "褐红色"}
];
动态生成颜色值
根据获取到的 AQI 数值,动态匹配对应的十六进制颜色代码:
function getColorByAqi(aqi) {
var aqival = parseInt(aqi);
if (aqival >= 0 && aqival <= 50) return "#00FF00";
if (aqival >= 51 && aqival <= 100) return "#FFFF00";
if (aqival >= 101 && aqival <= 150) return "#FFA500";
if (aqival >= 151 && aqival <= 200) return "#FF0000";
if (aqival >= 201 && aqival <= 300) return "#800080";
if (aqival >= 300) return "#993366";
}
图例切换功能
为了兼容温度与空气质量两种展示模式,图例需要支持动态切换:
function initLegendAndColor(type) {
var legendData = new Array();
var colorArray = new Array();
var _targetDataList;
if (type == "tem") {
_targetDataList = weatherColorList;
} else {
_targetDataList = aqiColorList;
}
for (var i = 0; i < _targetDataList.length; i++) {
var _tempData = _targetDataList[i];
legendData.push({
label: "\xa0\xa0" + _tempData.name,
type: "rectangle",
radius: 12,
color: _tempData.color,
fillColor: _tempData.color,
fillOpacity: 0.8,
weight: 2
});
colorArray.push(_tempData.rgb);
}
if (type == "tem") {
DIY_BLUE_GREEN_YELLOW_RED_SCHEME = new MultiColorScheme('', -20, 45, colorArray);
} else {
DIY_BLUE_GREEN_YELLOW_RED_SCHEME = new MultiColorScheme('', 0, 360, colorArray);
}
initLegend(legendData);
}
数据加载与渲染
最后,通过 AJAX 请求获取数据,解析 GeoJSON 并在地图上绘制区域图层及标注点:
// 展示 AQI 空气质量数据
function previewAQI(pid, provinceCode, name) {
previewProvince(pid, name);
initLegendAndColor("aqi");
$.ajax({
type: "get",
url: ctx + "/met/province/weather/list/" + provinceCode + "?day=" + $("#startTime").val(),
data: {},
dataType: "json",
cache: false,
processData: false,
success: function(result) {
if (result.code == web_status.SUCCESS) {
$("#title_info").html(name + "空气质量<sub>更新时间:" + $("#startTime").val() + "</sub>");
collisionLayer.clearLayers();
var dataArray = result.data;
if (dataArray != null && dataArray.length > 1) {
for (var i = 0; i < dataArray.length; i++) {
var areaData = dataArray[i];
var color = getColorByAqi(areaData.aqi);
// 绘制区域面
var areaLayer = L.geoJSON(JSON.parse(areaData.geomJson), {
style: {
color: color,
fillColor: color,
weight: 3,
"opacity": 0.65,
fillOpacity: 0.65
}
}).addTo(mymap);
// 添加标注图标
var myIcon = L.divIcon({
iconSize: null,
className: '',
popupAnchor: [5, 5],
shadowAnchor: [5, 5],
html: buildShowInfo(i, color, areaData, "aqi")
});
showLayerGroup.addLayer(areaLayer);
L.marker([areaData.lat, areaData.lon], { icon: myIcon }).addTo(collisionLayer);
}
collisionLayer.addTo(showLayerGroup);
}
}
},
error: function() {
$.modal.alertWarning("获取空间信息失败");
}
});
}
成果展示
整体分布
从整体来看,10 月 12 日这一天,全省可以分为三个比较明显的区域:
- 湘东北地区:如长沙、株洲、浏阳等地呈现红色,属于严重污染区域。
- 湘中和湘南:空气质量多为'良',占比超过一半。
- 湘西及湘东南:新晃、郴州等地空气质量达到'优'。
重点区域分析
深入观察重污染地区,以长沙市周边为例,株洲、湘潭、宁乡、岳阳的区域空气质量多为中度,部分区县甚至达到重度污染级别。浏阳等地区的 AQI 指数达到了 230,属于较高水平。
而在湘西北的龙山、花垣、永顺、桑植等区县,以及郴州的汝城、宜章、桂东等地,空气质量保持优质状态。
排名统计
污染严重前 10 区县
| 城市代码 | 城市名称 | 区县代码 | 区县名称 | 空气质量指数 |
|---|---|---|---|---|
| 430100 | 长沙市 | 430181 | 浏阳市 | 238.00 |
| 430200 | 株洲市 | 430281 | 醴陵市 | 238.00 |
| 430200 | 株洲市 | 430211 | 天元区 | 238.00 |
| 430200 | 株洲市 | 430212 | 渌口区 | 238.00 |
| 430200 | 株洲市 | 430202 | 荷塘区 | 238.00 |
| 430200 | 株洲市 | 430204 | 石峰区 | 238.00 |
| 430200 | 株洲市 | 430203 | 芦淞区 | 238.00 |
| 430100 | 长沙市 | 430182 | 宁乡市 | 220.00 |
| 430100 | 长沙市 | 430104 | 岳麓区 | 220.00 |
| 430100 | 长沙市 | 430112 | 望城区 | 220.00 |
可以看到,当日有 7 个区县的空气质量指数为 238,其中长沙市 4 个,株洲市 6 个。
质量优前 10 区县
| 城市代码 | 城市名称 | 区县代码 | 区县名称 | 空气质量指数 |
|---|---|---|---|---|
| 431200 | 怀化市 | 431227 | 新晃侗族自治县 | 29.00 |
| 431000 | 郴州市 | 431002 | 北湖区 | 39.00 |
| 431000 | 郴州市 | 431027 | 桂东县 | 39.00 |
| 431000 | 郴州市 | 431003 | 苏仙区 | 39.00 |
| 430700 | 常德市 | 430702 | 武陵区 | 40.00 |
| 431100 | 永州市 | 431122 | 东安县 | 40.00 |
| 431100 | 永州市 | 431103 | 冷水滩区 | 40.00 |
| 431100 | 永州市 | 431129 | 江华瑶族自治县 | 40.00 |
| 433100 | 湘西土家族苗族自治州 | 433130 | 龙山县 | 42.00 |
| 430800 | 张家界市 | 430811 | 武陵源区 | 44.00 |
当日省内空气质量最好的是怀化市的新晃自治县,AQI 仅为 29。
总结
通过将空气质量数据与地理空间信息相结合,我们构建了一个动态、交互的空气质量展示平台。用户可以在地图上清晰看到各地的空气质量等级,色彩鲜明的分区标识让污染状况一目了然。平台支持缩放地图、切换时间尺度及查询特定区域数据,这种高度的交互性有助于用户更好地理解环境状况。


