ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

WebGIS空气质量可视化实战:Leaflet+ECharts构建湖南省级监测系统源码解析

WebGIS空气质量可视化实战:Leaflet+ECharts构建湖南省级监测系统源码解析 简介这是一份面向 WebGIS 入门者、前端开发者及环保数据分析人员的可运行源码包。项目以湖南省空气质量为实际案例演示了从百度天气接口获取实时空气质量数据再通过 Leaflet 实现 WebGIS 可视化展示的完整流程包含省内中、重污染地区与优质环境区域的分布对比以及污染严重和空气质量优的前10名区县排名。压缩包共3个文件整体仅7KB以 HTML 单页应用为核心辅以 inscode 在线环境配置与 gitignore 忽略规则文件结构轻量、打开即用便于快速查看页面效果并进行定制改造。已有46人学习下载这份源码包。读者可从中掌握天气数据请求与解析、Leaflet 地图叠加、分级设色、排名榜单渲染等关键实现技巧并可将数据源替换为其他地区接口复用于课程设计、毕业设计或地方环境监测可视化项目。 先说一个背景这两年做环境数据展示的项目特别多空气质量、水质监测、噪声监测五花八门。但真正能落地的很少很多项目要么打开要配半天环境要么数据写死在地图上一动不能动。我手上这个“湖南空气质量WebGIS可视化”项目是实打实把WebGIS、数据可视化和可运行源码三者揉在一起做的身边不少同事拿着源码改一改就接到自己项目里用了。这篇文章就把整个实现过程、技术选型和踩坑记录完整写出来给准备做类似可视化项目的朋友一个参考。这个项目解决的核心问题很明确让没有GIS基础的人也能在浏览器里直观看到湖南省各市州的空气质量实况和变化趋势。它不是简单地把数据塞进表格而是把地图、空气质量指数AQI分级、时间轴联动、统计图表全部串起来形成一套交互完整的可视化系统。不管是做环境监测汇报、政务大屏还是毕业论文里的系统演示这套源码都够用。如果你正准备做WebGIS相关的毕业设计、数据可视化课程项目或者要快速搭一个省级区域的环境监测原型系统这篇文章可以直接帮你避开大部分弯路。下面按我的实际开发顺序来拆解。1. 整体设计思路为什么选WebGIS而不是纯图表先回答一个我经常被问的问题这种空气质量数据用ECharts画个柱状图、折线图不就行了吗为什么要引入地图1.1 核心需求解析空间属性决定技术路线空气质量数据有很强的空间属性——“湖南哪个市污染最重”“长株潭一带和湘西的差异有多大”这些问题不放到地图上根本看不出来。柱状图只能告诉你数值排序不能告诉你地理分布。而WebGIS天然擅长解决“数据位置”的关联展示这也是我坚定不移选这个方向的原因。另一个考虑是展示效率。一张湖南省地图14个市州按AQI颜色一铺哪个区域红、哪个区域绿扫一眼就清楚领导汇报时也愿意看。相比表格和普通图表这种方式信息传递效率高一个量级。1.2 技术选型Leaflet ECharts组合的取舍市面上的WebGIS方案大致分三类Cesium三维场景、OpenLayers功能全面、Leaflet轻量灵活。我做这个项目选的是Leaflet ECharts的组合。原因有三Leaflet 只有40KB左右对普通浏览器极其友好不需要重型GIS服务器一个静态页面就能跑起来ECharts 的地图注册机制可以直接对接GeoJSON用它来绘制湖南各市州边界非常顺手有待实践其实用Leaflet做边界绘制更顺滑但这里我选择了ECharts统计图配Leaflet底图的混合方案两者都是MIT协议对源码分发、二次开发没有限制如果你项目里已有天地图、高德或百度底图需求Leaflet也都有对应插件扩展性足够。唯一要提的是视频宣传或大屏展示要求高的话可以考虑Cesium但本项目定位是“快速、可运行、易修改”轻量组合才是正确选择。1.3 预期效果与适用范围最终做出来的系统打开浏览器就是一张湖南地图上面铺满14个市州的点位。点一个市弹窗显示AQI数值、首要污染物、等级。底部是时间轴拖动滑块可以看到不同日期的空气质量变化右侧是统计面板展示全省的优良天数比例、月度趋势。适用范围既有演示汇报也适合教学。学生用这套源码可以快速理解WebGIS的数据流——从数据获取、空间匹配到图层渲染几行代码就能看见效果。2. 核心实现细节数据结构、图层渲染与联动交互2.1 数据结构设计GeoJSON和属性表怎么配合这套系统里有两类核心数据一个是湖南省各市州的边界GeoJSON一个是每个市州的空气质量监测数据。边界数据长这样{ type: FeatureCollection, features: [ { type: Feature, properties: { name: 长沙市, adcode: 430100 }, geometry: { type: MultiPolygon, coordinates: [...] } } ] }空气质量数据则是一张平铺的属性表字段为日期、城市、AQI、PM2.5、PM10、首要污染物等。前端拿到属性表后按城市名和日期字段去匹配GeoJSON里的要素这就实现了“空间数据业务数据”的关联。这里有一个实操要点城市名的匹配必须统一比如GeoJSON里是“长沙市”数据表里也必须是“长沙市”不能写成“长沙”或“湖南长沙”。我一开始就是因为这个匹配问题排查了半天地图上不显示数据。建议在数据预处理阶段就清洗统一行政区划名称。2.2 Leaflet底图加载和地图初始化Leaflet的初始化代码非常简洁只需要指定一个div容器和中心点坐标就行const map L.map(map, { center: [27.6, 111.9], zoom: 7, zoomControl: true });然后加载一个合适的底图瓦片。这里我用的是高德地图的瓦片服务注意需要申请Key也可以直接用OpenStreetMap的公开瓦片L.tileLayer(https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png, { attribution: © OpenStreetMap contributors }).addTo(map);这里提醒一下底图选择不仅要看清晰度还要考虑偏色。我最初用了一个偏暗的地形图结果AQI分级色绿色、黄色、红色在上面对比度很低领导演示时直接说看不清。后来换成浅色底图效果立刻不一样。给大屏或汇报项目做底图优先选浅色、低饱和度的。2.3 空气质量点位渲染和分级着色这是可视化最核心的部分也是这个项目最有看点的地方。AQI分级色我参考的是《环境空气质量指数AQI技术规定》HJ 633—2012AQI范围等级颜色说明0-50优绿色适宜户外活动51-100良黄色可正常活动101-150轻度污染橙色敏感人群减少外出151-200中度污染红色减少户外活动201-300重度污染紫色避免户外活动300严重污染褐红色尽量避免外出在代码里我做了一个函数来返回对应颜色function getAQIColor(aqi) { if (aqi 50) return #00e400; if (aqi 100) return #ffff00; if (aqi 150) return #ff7e00; if (aqi 200) return #ff0000; if (aqi 300) return #99004c; return #7e0023; }渲染时我用了圆形Marker来表示各市州空气质量。半径大小映射AQI值AQI越大圆越大颜色映射分级色。这样一套规则下来地图上信息的视觉呈现就是一目了然的双变量映射L.circleMarker([lat, lng], { radius: Math.max(aqi / 8, 8), color: #fff, weight: 1, fillColor: getAQIColor(aqi), fillOpacity: 0.8 }).bindPopup(content).addTo(layerGroup);这里注意radius的计算方式。直接用AQI值作为radius最大值500会让圆点太大所以除以8再兜底最小8像素这样既保留了数值差异感又不会互相遮挡。2.4 时间轴联动从静态展示到动态演变如果只是展示某一天的空气质量分布那这个系统的价值就大打折扣了。我在底部加了一个时间滑动条拖到不同日期地图上的点位颜色和大小会实时更新。实现思路是预加载所有日期的数据存成一个对象const dataCache {}; dataCache[2024-11-01] [ { city: 长沙, aqi: 78, pm25: 55 }, { city: 株洲, aqi: 92, pm25: 66 }, // ... ];拖动时间轴时触发事件重新渲染全部点位slider.addEventListener(input, function() { const date dateList[this.value]; updateMarkers(dataCache[date]); updateStatsPanel(dataCache[date]); });联动还有一个细节右侧统计面板和ECharts图表也绑定了同一个时间状态这样“地图-统计图-列表”三者始终同步演示起来非常加分。很多静态可视化和这个的区别就在于有没有这层联动逻辑。3. 可视化组件搭建图表、弹窗和布局优化3.1 ECharts统计面板不只是画个折线图ECharts在这个项目里承担了统计图表的职责。我做的不是简单的各城市AQI排名条形图而是两大类全省月度AQI趋势折线图各城市首要污染物占比环形图第一个图用来反映整体走势第二个图用来展示污染结构。比如长沙市的首要污染物是PM2.5还是臭氧环形图能直接告诉你占比。const trendChart echarts.init(document.getElementById(trendChart)); trendChart.setOption({ xAxis: { type: category, data: monthList }, yAxis: { type: value, name: AQI }, series: [{ data: monthAvgData, type: line, smooth: true, areaStyle: { opacity: 0.2 } }] });这里我踩过一个坑ECharts实例在容器被隐藏或尺寸变化时会渲染异常尤其是嵌在大屏布局里、旁边有折叠面板时。解决办法是在窗口resize时调用chart.resize()并且用nextTick延迟初始化隐藏的图表容器。3.2 弹窗内容设计数据展示的最后一公里点地图上的圆点弹出的信息窗口是用户感知数据的第一界面。我的popup里放了四样东西城市名称、AQI数值与等级、PM2.5 和 PM10 数值、首要污染物。模板用字符串拼接const content div classpopup-card h3${data.city}/h3 pAQI: strong${data.aqi}/strong (${level})/p pPM2.5: ${data.pm25} μg/m³/p pPM10: ${data.pm10} μg/m³/p p首要污染物: ${data.primary}/p /div ;弹窗样式需要注意一下默认Leaflet弹窗的样式很素需要覆写CSS让它看起来更像一个“卡片”而不是浏览器默认的提示框。我给弹窗加了圆角、阴影和自定义字体整体质感提升明显。3.3 图例和辅助信息专业性的细节体现地图右下角我固定放了一个AQI分级图例。这个图例非常重要它不仅是视觉装饰更是数据解读的规则说明。没有图例用户看到不同颜色的圆点只能靠猜。实现方式很简单用Leaflet的L.control自定义一个控件const legend L.control({ position: bottomright }); legend.onAdd function() { const div L.DomUtil.create(div, legend); div.innerHTML divspan stylebackground:#00e400/span优/div divspan stylebackground:#ffff00/span良/div divspan stylebackground:#ff7e00/span轻度/div divspan stylebackground:#ff0000/span中度/div divspan stylebackground:#99004c/span重度/div divspan stylebackground:#7e0023/span严重/div ; return div; }; legend.addTo(map);这一块建议大家都保留很多可视化项目最终被吐槽“看不懂颜色”就是缺了这个细节。4. 数据获取思路与可运行源码的组织方式4.1 数据来源模拟数据、公开API与爬虫的取舍这个项目的实践数据我用了公开空气质量接口的样例行。大家在复现时有三种数据来源可选公开API中国环境监测总站和部分天气平台有AQI接口但存在跨域和请求频率限制需要后端代理静态模拟数据代码里内置一份比较接近真实分布的模拟数据保证离线也能演示爬虫抓取谨慎使用爬取公开空气质量站点数据注意遵守robots协议且数据量小的情况下完全没必要我这套系统最终在源码里配的是静态数据 API 接口预留的方式。直接把数据文件放在public/data/目录下前端通过fetch加载。同时留了一个api.js模块后续如果要接真实接口把请求地址和参数填进去就行。这种设计是“可运行源码”最稳妥的形态——用户拿到代码后即使没有网络、没有数据库也能立刻看到效果想上生产改改数据源就能用。4.2 完整目录结构与运行环境项目基于Vue 3 Vite 构建前端环境非常干净不需要安装GIS服务端组件。目录结构如下air-quality-webgis/ ├── index.html ├── package.json ├── vite.config.js ├── public/ │ └── data/ │ ├── hunan.geojson │ └── air-quality.json └── src/ ├── main.js ├── App.vue ├── components/ │ ├── MapView.vue │ ├── StatsPanel.vue │ ├── TimeSlider.vue │ └── LegendControl.vue ├── utils/ │ ├── aqi.js │ └── fetchData.js └── styles/ └── main.cssMapView.vue 负责地图相关的所有逻辑StatsPanel.vue 管图表TimeSlider.vue 管时间轴组件间通过事件总线或全局状态通信。这种拆分的好处是如果你只想单独用地图模块直接取 MapView.vue 就行不会牵连图表逻辑。4.3 启动和打包命令复现这个项目只需要三个命令npm install npm run dev npm run build第一行安装依赖第二行启动开发服务器访问http://localhost:5173就能看到系统。第三行是打包命令输出到dist/目录可以直接扔到Nginx、Apache或者静态托管平台上。开发调试时Vite的热更新响应非常快改完代码浏览器立即生效。这个体验对反复调整地图样式、弹窗布局来说很重要。5. 常见问题与排查经验这些坑我踩过5.1 地图不显示或白屏八成是底图瓦片请求被浏览器拦截了。跨域问题在本地开发时最常出现打开控制台Network面板能看到红色报错信息。解决办法检查底图URL是否使用HTTPS换用支持跨域的底图服务在vite.config.js里配置 devServer 的 proxy 代理5.2 GeoJSON加载后行政区划不显示如果是ECharts地图需要注册地图数据echarts.registerMap(hunan, geoJson)很多新手漏掉这一步。Leaflet这边则要注意L.geoJSON(data).addTo(map)的data是否已经是一个对象而非字符串。另一个常见的低级错误是GeoJSON坐标超出湖南范围导致地图边界被拉伸到奇怪的位置。排查时可以打印一下layer.getBounds()确认边界是否在[北纬24度-30度东经108度-114度]这个区间。5.3 弹窗打不开或数据不对弹窗内容绑定的是点位数据如果绑定的是初始化的第一个对象那无论点哪个城市弹窗都一样。检查一下bindPopup是否在遍历循环中正确传递了当前项而不是直接用var声明导致的闭包变量共享。ES6里用let声明循环变量可以解决这个问题但旧代码习惯用var我在一次维护时也踩过这个坑弹窗永远显示最后一条数据。5.4 自适应布局和大屏适配可视化项目经常要放到大屏上展示分辨率可能是1920x1080或更高。我的做法是外层容器百分比布局地图高度用calc(100vh - 其他高度)计算图表容器用ECharts的resize监听。对于更强的大屏适配可以上transform: scale()方案但目前的轻量系统按百分比布局已经足够。最后再分享一个扩展技巧接入实时数据源这套系统的架构决定了扩展实时数据非常方便。你只需要在api.js里加一个轮询函数每5分钟拉取一次最新的空气质量数据然后调用updateMarkers()刷新点位就行setInterval(async () { const latest await fetchLatestData(); updateMarkers(latest); }, 5 * 60 * 1000);如果数据变动的频率不高还可以用WebSocket做服务端推送但那样需要额外的后端支持。实测下来5分钟轮询已经能满足大多数环境监测场景的需求而且实现成本极低。我个人的体会是这种“GIS 数据可视化 前端工程化”的组合在未来的环境监测、智慧城市、农业气象等领域会越来越常用学会这套技术栈不只是能做一个空气质量系统很多类似场景都能复用。希望这篇内容能帮正在做类似项目的朋友少走弯路如果有其他问题欢迎在评论区交流我看到了都会回复。本文还有配套的精品资源点击获取
返回列表