项目介绍

智慧天气数据分析系统面向气象研究人员、环保部门和公众用户,实现了多源气象数据采集、历史数据分析和气象灾害预警功能。

项目采用Python+Flask+MySQL+Nginx架构,前端使用HTML+CSS+JavaScript构建响应式界面,后端通过Flask处理API请求和业务逻辑,MySQL存储气象历史数据和分析结果,并且引入了机器学习算法(随机森林和支持向量机)优化气象预测准确率。

参与模块:数据采集和数据清理、基于机器学习的气象预测算法、结合历史数据的气象灾害预警模型、ECharts可视化仪表盘(包含温度变化曲线、降水统计图和分布热力图)、系统部署采用Docker容器化技术,通过Nginx实现负载均衡和静态资源分发。

帆软大屏开发

数据表

ER图

1. 气象站点表 (dim_weather_station)

字段名 类型 描述 键类型 示例值
station_id VARCHAR2(10) 气象站唯一编码 主键 GX001
station_name VARCHAR2(50) 气象站名称 南宁站
city VARCHAR2(20) 所属城市 南宁
latitude NUMBER 纬度坐标 22.82
longitude NUMBER 经度坐标 108.36

作用:存储气象站点基本信息,作为其他表的外键关联基础。

1
2
3
4
5
6
7
8
-- 1. 气象站点维度表 (主维度表)
CREATE TABLE dim_weather_station (
station_id VARCHAR2(10) PRIMARY KEY,
station_name VARCHAR2(50),
city VARCHAR2(20),
latitude NUMBER,
longitude NUMBER
);

2. 实时天气观测表 (fact_weather_observation)

字段名 类型 描述 键类型 示例值
observation_id NUMBER 观测记录ID 主键 1
station_id VARCHAR2(10) 气象站ID 外键 GX001
observation_time TIMESTAMP 观测时间 2025-06-20 14:00
temperature NUMBER 温度(℃) 28.5
humidity NUMBER 湿度(%) 65
precipitation NUMBER 降水量(mm) 0.0

作用:存储实时天气数据,每分钟更新一次。

1
2
3
4
5
6
7
8
9
-- 2. 实时天气观测表 (主事实表)
CREATE TABLE fact_weather_observation (
observation_id NUMBER PRIMARY KEY,
station_id VARCHAR2(10) REFERENCES dim_weather_station(station_id),
observation_time TIMESTAMP,
temperature NUMBER,
humidity NUMBER,
precipitation NUMBER
);

3. 天气预报表 (fact_weather_forecast)

字段名 类型 描述 键类型 示例值
forecast_id NUMBER 预报记录ID 主键 1
station_id VARCHAR2(10) 气象站ID 外键 GX001
forecast_time TIMESTAMP 预报时间点 2025-06-21 08:00
temp_high NUMBER 预测最高温(℃) 32
temp_low NUMBER 预测最低温(℃) 25

作用:存储未来天气预报数据。

1
2
3
4
5
6
7
8
9
-- 3. 天气预报表
CREATE TABLE fact_weather_forecast (
forecast_id NUMBER PRIMARY KEY,
station_id VARCHAR2(10) REFERENCES dim_weather_station(station_id),
forecast_time TIMESTAMP,
weather VARCHAR2(20),
temp_high NUMBER,
temp_low NUMBER
);

4. 历史天气统计表 (fact_historical_summary)

字段名 类型 描述 键类型 关联点
summary_id BIGINT 主键(自增) 主键 3001
station_id VARCHAR(10) 关联气象站 外键 GX001
date DATE 统计日期 非空 2025-06-19
avg_temp DECIMAL(4,1) 日均温(℃) 29.1
max_temp TINYINT 日最高温(℃) 35
min_temp TINYINT 日最低温(℃) 24
total_rainfall DECIMAL(6,1) 累计降水量(mm) 12.5
rainfall_hours NUMBER(3) 降雨时长(小时) 2
avg_humidity NUMBER(3) 平均湿度(%) 60
min_humidity NUMBER(3) 最小湿度(%) 30
weather_condition VARCHAR2(20) 主要天气状况 晴/雨/多云等
extreme_weather VARCHAR2(50) 极端天气事件 雷暴/冰雹/大雾等

作用:存储历史天气统计数据,按日汇总。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
-- 4. 历史天气统计表
CREATE TABLE fact_historical_summary (
summary_id NUMBER PRIMARY KEY,
station_id VARCHAR2(10) REFERENCES dim_weather_station(station_id),
summary_date DATE NOT NULL,
-- 温度指标
avg_temp NUMBER(4,1), -- 日平均温度
max_temp NUMBER(4,1), -- 日最高温度
min_temp NUMBER(4,1), -- 日最低温度
-- 降水指标
total_rainfall NUMBER(6,1), -- 日总降水量(mm)
rainfall_hours NUMBER(3), -- 降雨时长(小时)
-- 湿度指标
avg_humidity NUMBER(3), -- 平均湿度(%)
min_humidity NUMBER(3), -- 最小湿度(%)
-- 天气现象
weather_condition VARCHAR2(20), -- 主要天气状况(晴/雨/多云等)
extreme_weather VARCHAR2(50), -- 极端天气事件(雷暴/冰雹/大雾等)
-- 约束
CONSTRAINT uk_hist_unique UNIQUE (station_id, summary_date)
);

5. 气象灾害预警表 (dim_weather_alert)

字段名 类型 描述 键类型 示例值
alert_id VARCHAR2(20) 预警ID 主键 ALERT001
station_id VARCHAR2(10) 气象站ID 外键 GX001
alert_type VARCHAR2(20) 预警类型 暴雨预警
issue_time TIMESTAMP 发布时间 2025-06-20 13:30

作用:存储气象灾害预警信息。

1
2
3
4
5
6
7
-- 5. 气象灾害预警表
CREATE TABLE dim_weather_alert (
alert_id VARCHAR2(20) PRIMARY KEY,
station_id VARCHAR2(10) REFERENCES dim_weather_station(station_id),
alert_type VARCHAR2(20),
issue_time TIMESTAMP
);

大屏效果展示

实时天气(左上滚动屏)

数据库查询语句

1
2
3
4
5
6
7
8
9
10
SELECT
s.station_name AS "气象站名称",
s.city AS "所在城市",
o.observation_time AS "观测时间",
o.temperature AS "温度(℃)",
o.humidity AS "湿度(%)",
o.precipitation AS "降水量(mm)"
FROM fact_weather_observation o
JOIN dim_weather_station s ON o.station_id = s.station_id
ORDER BY o.observation_time DESC

滚动效果:添加事件(初始化后)写入如下代码:

1
2
3
setTimeout(function(){
_g().getWidgetByName('实时天气数据').startMarquee()
}, 500);

天气预报(右上角滚动屏)

数据库查询语句

1
2
3
4
5
6
7
8
9
10
SELECT
f.forecast_id AS "预报ID",
s.city AS "城市",
f.forecast_time AS "预报时间",
f.weather AS "天气",
f.temp_high AS "最高温度",
f.temp_low AS "最低温度"
FROM fact_weather_forecast f
JOIN dim_weather_station s ON f.station_id = s.station_id
ORDER BY f.forecast_time ASC

气象灾害预警(右下角滚动屏)
数据库查询语句

1
2
3
4
5
6
7
8
SELECT
a.alert_id AS "预警ID",
s.city AS "城市",
a.alert_type AS "预警类型",
a.issue_time AS "发布时间"
FROM dim_weather_alert a
JOIN dim_weather_station s ON a.station_id = s.station_id
ORDER BY a.issue_time DESC

地图

设置图表切换属性为按钮,实现点击切换展示降雨量、湿度、温度、气象站点

地图根据值设置渐变效果

其中降雨量、湿度、温度使用实时天气数据的查询语句结果,气象站点使用以下查询数据库语句:

1
2
3
4
5
6
7
8
SELECT 
station_id AS "站点ID",
station_name AS "气象站名称",
city AS "所属城市",
longitude AS "经度坐标",
latitude AS "纬度坐标",
longitude || ',' || latitude AS "经纬度坐标"
FROM dim_weather_station

左下图表

图表切换属性为轮播

自定义鼠标悬浮时的提示,可使用富文本

中下方查询模块

默认展示某日历史数据,通过设置城市、日期、类型三个参数可查询展示对应数据

四个类型需分别设置四个表单放置在相同位置,通过类型下拉框选中的值(控件名stype)控制展示哪张表,每张表选中所有单元格并添加条件属性使行高为0(隐藏),条件公式为:$stype!=”历史数据” (以历史数据查询结果表为例,当类型下拉控件为选择“历史数据”时,隐藏该表)

各表使用的数据库查询语句如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
-- 历史数据表单中的数据集
SELECT *
FROM fact_historical_summary h
JOIN dim_weather_station s ON h.station_id = s.station_id
WHERE 1=1
${if(city == "",""," AND s.city IN ('"+city+"')")}
${if(date == "",""," AND h.summary_date = TO_DATE('"+date+"','YYYY-MM-DD')")}

-- 天气预报表单中的数据集
SELECT *
FROM fact_weather_forecast f
JOIN dim_weather_station s ON f.station_id = s.station_id
WHERE 1=1
${if(city == "",""," AND s.city IN ('"+city+"')")}
${if(date == "",""," AND TRUNC(f.forecast_time) = TO_DATE('"+date+"','YYYY-MM-DD')")}

-- 灾害预警表单中的数据集
SELECT *
FROM dim_weather_alert a
JOIN dim_weather_station s ON a.station_id = s.station_id
WHERE 1=1
${if(city == "",""," AND s.city IN ('"+city+"')")}
${if(date == "",""," AND TRUNC(a.issue_time) = TO_DATE('"+date+"','YYYY-MM-DD')")}

-- 气象站点表单中的数据集
SELECT *
FROM dim_weather_station
WHERE 1=1
${if(city == "",""," AND city IN ('"+city+"')")}

${if(city == “”,””,” AND city IN (‘“+city+”‘)”)}
为确保以上语句生效需要设置城市下拉复选框的返回值分隔符为','
否则可能需要改为${if(city == “”,””,” AND s.city IN (‘“+city.replace(“,”,”‘,’”)+”‘)”)}

没有数据提示

以历史数据为例,在数据栏下添加一栏合并居中的“没有数据”提示,并添加行高为0的条件属性,条件为:len(A2)!=0 (即当A2有数据时,隐藏该提示)

填报按钮

需要另外创建四个对应类型的填报表单,模板数据集同使用以上查询功能的查询语句
点击填报按钮后获取城市、日期、类型的值,并打开对应表单对话框
填报按钮的事件代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
// 获取当前选择的查询类型
var selectedType = this.options.form.getWidgetByName("stype").getValue();

// 安全获取城市值(兼容单选/多选/空值)
var cityValue = this.options.form.getWidgetByName("scity").getValue();
var cityParam = "";

// 处理不同类型的返回值
if (Array.isArray(cityValue)) {
cityParam = cityValue.join(",");
} else if (cityValue && typeof cityValue === "string") {
cityParam = cityValue;
} // 空值保持为空字符串

// 获取当前选择的日期(直接使用字符串值)
var selectedDate = this.options.form.getWidgetByName("sdate").getValue();
var dateParam = selectedDate || "";

// 确保日期格式正确(去除时间部分)
if (dateParam && dateParam.length > 10) {
dateParam = dateParam.substring(0, 10);
}

// 根据查询类型打开对应表单
var formPath = "";
var formTitle = "";

switch(selectedType) {
case "历史数据":
formPath = "历史数据填报.cpt";
formTitle = "历史数据填报";
break;
case "天气预报":
formPath = "天气预报填报.cpt"; // 确保添加了.cpt扩展名
formTitle = "天气预报填报";
break;
case "灾害预警":
formPath = "灾害预警填报.cpt";
formTitle = "灾害预警填报";
break;
case "气象站点":
formPath = "气象站点填报.cpt";
formTitle = "气象站点填报";
break;
default:
FR.Msg.alert("提示", "请先选择查询类型");
return;
}

// 构建参数对象
var params = {
city: cityParam,
date: dateParam
};

// 打开表单对话框
FR.doHyperlinkByPost({
url: "${servletURL}?reportlet=" + formPath + "&op=write",
para: params,
target: "_dialog",
feature: {
title: formTitle,
width: 1200,
height: 700
}
});

每个表单需要在 模板-模板参数 中设置两个模板参数(city、date)

各表单设计(同6.17填报设计)

效果展示