前言
地理信息系统(GIS)技术与 Web 技术的融合为地理信息可视化提供了便利。WebGIS 能够将地理空间数据直观呈现,天气数据的可视化对气象预报等领域具有重要意义。本文将从 WebGIS 的视角出发,探讨如何利用 Leaflet 这一开源 JavaScript 库,实现省域区县天气的可视化。

一、空间数据基础
本节介绍相关的空间数据基础,涉及省级空间范围、区县行政空间范围以及区县驻地等信息的检索。为了在 WebGIS 展示时将天气数据直接与行政区划关联,我们在天气表中预留了行政区划编码的字段。
1、省域空间检索
省域信息用于省级行政范围的展示,以及通过百度天气接口获取具体天气信息时作为索引。省域信息及表结构如下:
select * from biz_province;
上图中返回的 code 即为该省份对应的行政区划代码,如:130000 表示河北省。查询下级区县信息需使用区县信息表,查询语句如下(以河北省为例):
select * from biz_area t where t.province_code = '130000';
通过 SQL 查询可获取指定省份的下属区县信息,后续根据该区县 code 获取天气信息。
2、区县天气信息检索
涉及的数据表包括实时天气信息表、区县信息表及城市信息表。
| 序号 | 表名 | 说明 |
|---|---|---|
| 1 | biz_weather_now | 实时天气信息表 |
| 2 | biz_area | 区县信息表 |
| 3 | biz_geographic_name | 城市信息表 |
以下是区县天气信息检索的 SQL(以查询 2025 年 8 月 17 日的湖南省 430000 的区县天气信息为例):
SELECT t2.*, T.province_code, T.province_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-08-17' 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;
二、天气数据简介
天气变化的复杂性和不确定性使得准确、及时地获取和展示天气信息成为挑战。传统的天气预报方式往往只能提供较为宏观的天气趋势,对于具体的区县级天气状况描述不够细致。这里的天气信息数据源以百度天气为例,通过查询获取区域内的天气信息。
1、省域天气数据获取
核心逻辑是在后台查询区县并获取对应的天气信息,批量处理省级数据。关键 Java 代码如下:
/** * - 批量处理省级数据 * @throws InterruptedException */ @Test public void bdWeather2PGWithProvince() throws InterruptedException { String provinceCode = "430000"; Random random = new Random(); QueryWrapper<Area> queryWrapper = new QueryWrapper<Area>(); queryWrapper.like("province_code", provinceCode); List<Area> areaList = areaService.list(queryWrapper); if(StringUtils.isNotEmpty(areaList)) { for (Area area : areaList) { String areaCode = area.getAreaCode(); HttpResponse<String> result = baiduWeatherApiService.getWeather(areaCode, DATA_TYPE); Gson gson = new Gson(); BdWeatherDTO bdWeatherInfo = gson.fromJson(result.getBodyResult(), BdWeatherDTO.class); WeatherInfoDTO bdResult = bdWeatherInfo.getResult(); //将天气信息持久化到数据库中 if(null != bdResult) { Long weatherId = IdWorker.getId(); bdResult.getWeatherNow().setPkId(weatherId); bdResult.getWeatherNow().setLocationCode(areaCode); weatherService.insertWeatherInfo(bdResult); } Thread.sleep(1500 + random.nextInt(1000)); } } }
运行以上代码即可实现省域区县天气的入库处理。
2、区县名称不一致
由于区县信息和城市信息的采集和整理的时候可能存在不一致的情况。比如在行政区划调整的过程,有的县级名称可能会改成区。为了保证在查询能关联查到,因此需要将两张表的数据进行清理统一,修改成统一的名称即可。
三、SpringBoot 后台实现
本文采用 SpringBoot 框架为 WebGIS 提供后台服务,主要涉及的功能有天气数据查询的实现。主要从后台的天气的数据查询实现,以及 Java 的控制层的具体实现两个方面来进行。
1、Java 后台天气数据查询
使用 Java 进行区县天气数据查询比较简单,首先基于 MybatisPlus 需要定义数据视图对象,关键的 Java 代码如下所示:
package com.yelang.project.meteorology.mapper; import java.util.List; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Select; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.yelang.project.meteorology.domain.AreaWeatherVO; import com.yelang.project.meteorology.domain.WeatherNow; public interface WeatherNowMapper extends BaseMapper<WeatherNow>{ final static String GET_WEATHER_BYPROVINCE_ANDDAY = "<script>" + " 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' ) = #{day} AND T.province_code = #{provinceCode} " + " 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 " + "</script>"; /** * - 根据省份和日期查询区县城市天气视图对象列表 * @return */ @Select(GET_WEATHER_BYPROVINCE_ANDDAY) List<AreaWeatherVO> getWeatherByProvinceAndday(@Param("provinceCode") String provinceCode,@Param("day") String day); }
查询结果对应的视图对象代码如下,为了最大程度的实现对象的复用,视图对象集成了实时天气对象 JavaBean:
package com.yelang.project.meteorology.domain; import java.io.Serializable; import com.baomidou.mybatisplus.annotation.TableField; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.ToString; @Data @ToString(callSuper=true)//callSuper=true 表示输出父类属性 @EqualsAndHashCode(callSuper=false) public class AreaWeatherVO extends WeatherNow implements Serializable{ private static final long serialVersionUID = -7559774548761847068L; @TableField(exist = false,value= "province_code") private String provinceCode; @TableField(exist = false,value= "province_name") private String provinceName; @TableField(exist = false,value= "city_code") private String cityCode; @TableField(exist = false,value= "city_name") private String cityName; @TableField(exist = false,value= "area_name") private String areaName; @TableField(exist = false) private String geomJson; private String lat; private String lon; }
业务逻辑层的实际这里比较简单不做详细叙述。
2、控制层实现
控制层作为接收前端的请求并且返回响应数据的重要组件,这里主要提供展示界面的跳转以及区县天气数据列表的获取,核心代码如下:
package com.yelang.project.meteorology.controller; import java.util.List; import org.apache.shiro.authz.annotation.RequiresPermissions; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import com.yelang.framework.web.controller.BaseController; import com.yelang.framework.web.domain.AjaxResult; import com.yelang.project.meteorology.domain.AreaWeatherVO; import com.yelang.project.meteorology.service.IWeatherNowService; @Controller @RequestMapping("/met/province/weather") public class ProvinceWeatherController extends BaseController{ private String prefix = "meteorology/weather"; @Autowired private IWeatherNowService weatherNowService; @RequiresPermissions("met:province:weather:map") @GetMapping("/province") public String province(){ return prefix + "/province"; } @RequiresPermissions("met:province:weather:list") @GetMapping("/list/{pcode}") @ResponseBody public AjaxResult ewsnProvinceList(@PathVariable("pcode") String pcode){ String day = "2025-08-17"; List<AreaWeatherVO> dataList = weatherNowService.getWeatherByProvinceAndday(pcode,day); return AjaxResult.success().put("data", dataList); } }
四、WebGIS 前端实现
在众多可用于 WebGIS 开发的工具和库中,Leaflet 凭借其轻量级、易上手、功能强大且高度可定制的特点脱颖而出,成为众多开发者在地理信息可视化项目中的首选工具之一。本节将重点介绍如何使用 Leaflet 来进行 WebGIS 开发和实现。在面向天气的应用中,主要进行气温颜色和数据的展示两个部分。
1、气温颜色及图例初始化
为了直观的展示天气的气温信息,我们首先对气温的值、颜色、气温描述等信息进行定义,这是后续的可视化展示的基础。气温颜色色带(colormap)是一种常用的工具,用于将数值范围映射到颜色范围。以下是一个详细的气温颜色色带表格,包括颜色的十六进制代码和对应的气温范围。
| 气温范围 (℃) | 颜色描述 | 十六进制颜色代码 | RGB 颜色表示 (R, G, B) |
|---|---|---|---|
| 45℃以上 | 深红色 | #8B0000 | (139, 0, 0) |
| 40℃ - 45℃ | 红色 | #FF0000 | (255, 0, 0) |
| 35℃ - 40℃ | 橙红色 | #FF4500 | (255, 69, 0) |
| 30℃ - 35℃ | 橙色 | #FFA500 | (255, 165, 0) |
| 25℃ - 30℃ | 黄色 | #FFFF00 | (255, 255, 0) |
| 20℃ - 25℃ | 浅绿色 | #90EE90 | (144, 238, 144) |
| 15℃ - 20℃ | 绿色 | #00FF00 | (0, 255, 0) |
| 10℃ - 15℃ | 青色 | #00FFFF | (0, 255, 255) |
| 5℃ - 10℃ | 浅蓝色 | #ADD8E6 | (173, 216, 230) |
| 0℃ - 5℃ | 蓝色 | #0000FF | (0, 0, 255) |
| -5℃ - 0℃ | 深蓝色 | #4169E1 | (65, 105, 225) |
| -10℃ - -5℃ | 紫色 | #800080 | (128, 0, 128) |
| -15℃ - -10℃ | 深紫色 | #4B0082 | (75, 0, 130) |
| -20℃ - -15℃ | 黑色 | #000000 | (0, 0, 0) |
在 JavaScript 中定义以上信息:
//气温及颜色配置 var weatherColorList = [ {name:"-20℃ - -15℃",color:"#000000",rgb:new Color(0,0,0),colorDesc:"黑色"}, {name:"-15℃ - -10℃",color:"#4B0082",rgb:new Color(75,0,130),colorDesc:"深紫色"}, {name:"-10℃ - -5℃",color:"#800080",rgb:new Color(128,0,128),colorDesc:"紫色"}, {name:"-5℃ - 0℃",color:"#4169E1",rgb:new Color(65,105,225),colorDesc:"深蓝色"}, {name:"0℃ - 5℃",color:"#0000FF",rgb:new Color(0,0,255),colorDesc:"蓝色"}, {name:"5℃ - 10℃",color:"#ADD8E6",rgb:new Color(173,216,230),colorDesc:"浅蓝色"}, {name:"10℃ - 15℃",color:"#00FFFF",rgb:new Color(0,255,255),colorDesc:"青色"}, {name:"15℃ - 20℃",color:"#00FF00",rgb:new Color(0,255,0),colorDesc:"绿色"}, {name:"20℃ - 25℃",color:"#90EE90",rgb:new Color(144,238,144),colorDesc:"浅绿色"}, {name:"25℃ - 30℃",color:"#FFFF00",rgb:new Color(255,255,0),colorDesc:"黄色"}, {name:"30℃ - 35℃",color:"#FFA500",rgb:new Color(255,165,0),colorDesc:"橙色"}, {name:"35℃ - 40℃",color:"#FF4500",rgb:new Color(255,69,0),colorDesc:"橙红色"}, {name:"40℃ - 45℃",color:"#FF0000",rgb:new Color(255,0,0),colorDesc:"红色"}, {name:"45℃以上",color:"#8B0000",rgb: new Color(139,0,0),colorDesc:"深红色"} ];
在 WebGIS 中需要使用色带即 colorMap 的方式对气温颜色进行定义,同时在地图中展示相应的图例(需要注意的是,这里的气温极值取 -20 和 45 度,这两个值在实际生活中可能有所调整,大家自己灵活处理),定义的关键代码如下:
var DIY_BLUE_GREEN_YELLOW_RED_SCHEME; $(document).ready(function () { initSidebar(); var legendData = new Array(); var colorArray = new Array(); for(var i =0;i<weatherColorList.length;i++){ var _tempData = weatherColorList[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); DIY_BLUE_GREEN_YELLOW_RED_SCHEME = new MultiColorScheme('', -20,45 ,colorArray); initLegend(legendData); });
2、气温数据展示实现
为了方便对气温数据进行展示,同时考虑到区县数量是比较多的,因此在标绘展示时不要让中文标签被遮挡住,因此这里我们采用碰撞检测组件来进行辅助提升可视化效果,碰撞组件定义如下:
var collisionLayer = L.LayerGroup.collision({margin:2});
定义好碰撞组件后,接下来可以实现对气温数据展示,关键代码如下:
function previewWeather(pid,provinceCode,name){ previewProvince(pid,name); $.ajax({ type:"get", url:ctx + "/met/province/weather/list/" + provinceCode, data:{}, dataType:"json", cache:false, processData:false, success:function(result){ if(result.code == web_status.SUCCESS){ $("#title_info").html(name+"天气实况<sub>更新时间:20250817" +"</sub>"); collisionLayer.clearLayers(); var dataArray = result.data; if(dataArray != null && dataArray.length > 1){ var legendData = new Array(); for(var i =0;i< dataArray.length;i++){ var areaData = dataArray[i]; var color = makeColor(areaData.temp,-20,45,DIY_BLUE_GREEN_YELLOW_RED_SCHEME); 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) }); showLayerGroup.addLayer(areaLayer); //中心点位 L.marker([areaData.lat, areaData.lon], { icon: myIcon}).addTo(collisionLayer); collisionLayer.addTo(showLayerGroup); } } }, error:function(){ $.modal.alertWarning("获取空间信息失败"); } }); }
五、成果展示
本节将主要描述 WebGIS 的区县天气展示,这里我们从湖南省和西藏自治区两个地区来进行展示。湖南省应该是一个比较典型的气温高的省份,而西藏则是气温比较低的省份。同时可以对比以下实际的色温展示成果。
1、湖南省天气展示

从整体来看,湖南 8 月 17 日 8 点左右的全省气温信息比之前的要好多了(整体范围从 20 度到 29 度,均小于 23 度),温差变大,体感舒适度有所提高了。接下来我们从低温前五和高温前五来看下,首先看下低温前 5 是哪些区县:
湖南省 郴州市 桂东县 20.00 湖南省 衡阳市 南岳区 21.00 湖南省 郴州市 汝城县 21.00 湖南省 湘西土家族苗族自治州 花垣县 21.00 湖南省 株洲市 炎陵县 22.00
气温从高到低前 5 区县是:
湖南省 岳阳市 岳阳楼区 29.00 湖南省 岳阳市 云溪区 29.00 湖南省 益阳市 资阳区 29.00 湖南省 益阳市 赫山区 29.00 湖南省 岳阳市 君山区 29.00
2、西藏自治区天气展示

从整体来看,西藏自治区 8 月 17 日 8 点左右的全省气温信还是比较低,这跟其高海拔有关系(整体范围从 2 度到 21 度,跨度还是非常大的)。接下来我们从低温前五和高温前五来看下,首先看下低温前 5 是哪些区县:
西藏自治区 那曲市 嘉黎县 2.00 西藏自治区 山南市 错那县 4.00 西藏自治区 日喀则市 亚东县 4.00 西藏自治区 山南市 措美县 4.00 西藏自治区 昌都市 芒康县 5.00
气温从高到低前 5 区县是:
西藏自治区 林芝市 墨脱县 21.00 西藏自治区 林芝市 朗县 14.00 西藏自治区 林芝市 察隅县 14.00 西藏自治区 林芝市 波密县 14.00 西藏自治区 山南市 曲松县 14.00
可以看到,在西藏的林芝市,其气温跟湖南是差不多的,而且林芝市温度最高的是墨脱县。林芝市的气温是真不错,不愧是西藏江南。
六、总结
本文介绍了基于 Leaflet 和 WebGIS 实现天气可视化的流程与技巧。通过本文的阅读,读者能够对基于 Leaflet 的省域区县天气可视化有一个全面而深入的了解,并掌握实现这一目标的具体方法和技巧。


