前言
现代技术让我们有机会用新的方式重现旧时的记忆。本项目利用百度天气 API 获取实时数据,结合 WebGIS 技术构建一个具有 80 年代风格的天气预报系统。界面和播报形式保留复古元素,但数据来源保持现代准确性。

在开发过程中,我们面临几个主要挑战:如何整合地图可视化、如何处理高并发下的 API 调用限制以及如何还原经典的播报风格。通过 HTML5、CSS3、JavaScript 以及 Java 后端,我们实现了数据的无缝对接和高效展示。
一、省会城市信息构建
在进行可视化之前,首先需要查询全国省会城市的空间信息,以便后续调用天气接口。这里涉及数据库中的点位信息和省份信息表。
1、省会城市空间查询
使用 SQL 语句配合 PostGIS 函数进行查询,核心逻辑是筛选出标记为'省会城市'、'直辖市'或'首都'的记录,并关联省份表获取经纬度及 GeoJSON 数据。
SELECT T.NAME cityName, T.pinYin, T.bz, T.slx, tc.code provinceCode, tc.NAME provinceName, st_x(T.geom) cityLon, st_y(T.geom) cityLat, dict.dict_label provinceAbbreviations, st_asgeojson(tc.geom) geomJson
FROM biz_geographic_name T, biz_province tc, sys_dict_data dict
WHERE T.bz IN ('省会城市', '直辖市', '首都')
AND st_contains(tc.geom, T.geom)
AND dict.dict_value = tc.code
ORDER BY tc.code
执行后得到的结果包含城市名称、拼音、经纬度及省份代码,这些字段将作为后续天气查询的基础参数。
2、Java 后台查询
将上述 SQL 逻辑封装到 MyBatis Mapper 中,方便前端调用。Mapper 类核心代码如下:
static final String FIND_PROVINCEABBREVIATIONS_LIST = "<script>" + " SELECT T.name cityName,T.pinYin,T.bz,T.slx,tc.code provinceCode,tc.NAME provinceName, " + " st_x(T.geom) cityLon,st_y(T.geom) cityLat,dict.dict_label provinceAbbreviations, " + " st_asgeojson(tc.geom) geomJson " + " FROM biz_geographic_name T,biz_province tc,sys_dict_data dict " + " WHERE T.bz IN('省会城市', '直辖市', '首都') AND st_contains(tc.geom, T.geom) AND dict.dict_value = tc.code " + " order by tc.code " + "</script>";
@Select(FIND_PROVINCEABBREVIATIONS_LIST)
List<ProvinceAbbreviationsVo> findProvinceAbbreviations();
为了复用性,我们在 Service 层进一步封装了查询方法,并通过 Controller 暴露 RESTful 接口供第三方服务调用。
二、Java 省会城市天气查询
有了城市列表后,接下来根据经纬度调用百度天气接口获取实时预报。本节介绍接口集成及响应对象的处理。
1、与百度开放平台集成天气
定义 HTTP 接口方法,支持传入经纬度和坐标类型。注意经度在前、纬度在后,逗号分隔。
/**
* - 根据经纬度和坐标类型查询指定地点的天气信息
* @param location 经纬度,经度在前纬度在后,逗号分隔。支持类型:bd09mc/bd09ll/wgs84/gcj02
* @param data_type 请求数据类型。数据类型有:now/fc/index/alert/fc_hour/all,控制返回内容,默认值无
* @param coordtype 支持类型:wgs84/bd09ll/bd09mc/gcj02 默认值:wgs84
* @return
*/
@GetHttpInterface("/")
public HttpResponse<String> getByLocation(@QueryPar("location") String location, @QueryPar("data_type") String data_type, @QueryPar("coordtype") String coordtype);
调用实例中,我们遍历城市列表,逐个请求天气数据。为了避免触发接口的并发限制,在循环中加入了随机休眠时间。
@Test
public void testGetProvinceWeather() throws InterruptedException {
Random random = new Random();
List<LinkedHashMap<String, Object>> provinceWeatherData = new ArrayList<>();
List<ProvinceAbbreviationsVo> provinceList = geographicNameService.findProvinceAbbreviations();
Gson gson = new Gson();
for (ProvinceAbbreviationsVo vo : provinceList) {
String location = vo.getCityLon() + "," + vo.getCityLat();
HttpResponse<String> result = baiduWeatherApiService.getByLocation(location, DATA_TYPE, "wgs84");
if (StringUtils.isNotEmpty(result.getBodyResult())) {
LinkedHashMap<String, Object> map = new LinkedHashMap<>();
map.put("lat", vo.getCityLat());
map.put("lon", vo.getCityLon());
map.put("city", vo.getCityName());
BdWeatherDTO bdWeatherInfo = gson.fromJson(result.getBodyResult(), BdWeatherDTO.class);
WeatherInfoDTO weatherInfoDTO = bdWeatherInfo.getResult();
map.put("w_name", weatherInfoDTO.getWeatherNow().getText());
List<WeatherForecasts> forecasts = weatherInfoDTO.getForecasts();
if (StringUtils.isNotEmpty(forecasts)) {
WeatherForecasts today = forecasts.get(0);
map.put("temp", today.getLow() + "-" + today.getHigh());
WeatherForecasts tomorrow = forecasts.get(1);
Date tempDate = tomorrow.getDate();
String two = (tempDate.getMonth() + 1) + "-" + tempDate.getDate() + " " + tomorrow.getLow() + "-" + tomorrow.getHigh();
map.put("two", two);
}
provinceWeatherData.add(map);
}
Thread.sleep(1500 + random.nextInt(1000));
}
System.out.println(gson.toJson(provinceWeatherData));
}
2、响应对象属性介绍
为了方便前端渲染,我们将返回数据标准化为一个 JSON 对象结构,包含经纬度、城市名、当前天气、今日气温及明日预报。
| 序号 | 参数名称 | 说明 |
|---|---|---|
| 1 | lat | 纬度 |
| 2 | lon | 经度 |
| 3 | city | 城市名称 |
| 4 | w_name | 天气说明 |
| 5 | temp | 气温 |
| 6 | two | 明日天气 |
示例数据如下:
{
"lat":"39.903162481",
"lon":"116.401006456",
"city":"北京市",
"w_name":"晴",
"temp":"-2-10",
"two":"12-8 -4-5"
}
3、省会天气实况展示
程序运行后可生成包含全国省会城市两天预报的完整数据集,例如北京、上海、广州等地的实时状态均可通过此接口获取。
三、WebGIS 应用构建
数据准备就绪后,重点转向前端的可视化呈现。我们使用 Leaflet 框架加载地图,集成经典背景音乐,并实现城市轮播效果。
1、背景音乐集成
为了还原 80 年代氛围,我们引入了经典的天气预报背景音乐。在 HTML 中使用 <video> 标签播放音频,并通过 CSS 隐藏视频画面,仅保留声音。
<!-- 视频标签,设置为循环播放,初始时不自动播放 -->
<video loop muted>
<source src="./weather/music.mp4" type="video/mp4">
您的浏览器不支持 HTML5 视频。
</video>
/* 隐藏视频画面 */
video { display: none; }
页面加载后延迟 2 秒自动播放音乐,并将音量调整为 20% 以避免突兀。
var video = document.getElementById('backgroundVideo');
video.volume = 0.2;
window.onload = function() {
initWeather();
setTimeout(function() {
video.play();
video.muted = false;
preview(); // 开始城市轮播
}, 2000);
};
2、城市标记及天气展示
使用 Leaflet 在地图上标注所有省会城市位置,并在 Marker 上显示天气卡片。为了区分不同城市,我们为每个标记分配了随机颜色。
function initWeather(){
var collisionLayer = L.LayerGroup.collision({margin:3});
for(var i=0; i<dataJson.length; i++){
var marker = L.marker([dataJson[i].lat, dataJson[i].lon], {
icon: L.divIcon({
iconSize: null,
className: '',
popupAnchor:[5,5],
shadowAnchor:[5,5],
html: buildHtml(dataJson[i],i)
})
}).addTo(collisionLayer);
}
collisionLayer.addTo(map);
}
天气信息的 HTML 构建逻辑如下,注意修复了原代码中的语法错误:
function buildHtml(dataJson, index){
var html = '';
html += '<div style="color:' + getRandomColor() + '; animation: spaceInDown;">';
html += '<div><b>' + dataJson.city + ' ' + dataJson.w_name + ' ' + dataJson.temp + '℃</b><span></span></div>';
html += '<div>' + dataJson.two + ' ℃</div>';
html += '</div>';
return html;
}
function getRandomColor() {
var letters = '0123456789ABCDEF';
var color = '#';
for (var i = 0; i < 6; i++) {
color += letters[Math.floor(Math.random() * 16)];
}
return color;
}
3、城市轮播
为了让地图动起来,我们使用定时器每隔 3 秒切换一次中心点,模拟老式电视新闻中地图跳动的效果。
function preview(){
const intervalId = setInterval(() => {
if (currentIndex >= dataJson.length) {
currentIndex = 0;
}
const currentElement = dataJson[currentIndex];
if(dymicMarker != null){
map.removeLayer(dymicMarker);
}
dymicMarker = L.marker([currentElement.lat, currentElement.lon]).addTo(map);
setTargetSelect(dymicMarker);
console.log(`当前元素:${currentElement}`);
map.setView([currentElement.lat, currentElement.lon], 8);
currentIndex++;
}, 3000);
}
function setTargetSelect(e){
var i = 1;
var int = setInterval(() => {
if(!e._map) clearInterval(int);
if (i < -1) { i = 1; }
i = i - 0.08;
if (i < 0) e.setOpacity(i * -1);
else e.setOpacity(i);
}, 60);
}
4、成果展示
完成以上步骤后,即可得到一个带有背景音乐、动态地图标记及轮播功能的天气小应用。

四、总结
本教程展示了如何利用 Java 后端处理地理数据与天气 API,并结合 Leaflet 实现 WebGIS 可视化。从数据库的空间查询到前端的动画交互,整个流程涵盖了前后端协作的关键环节。通过这个项目,我们不仅实现了功能,也探索了如何用现代技术承载怀旧的情感体验。


