fix: 天气预测接口更替为开源的 Open-Meteo

- 默认情况下,将结合最适合的天气模型(auto)
- 预报 3天长度
This commit is contained in:
yarnom 2025-07-08 12:15:54 +08:00
parent d8456d165b
commit 7555c4c72b
2 changed files with 66 additions and 30 deletions

View File

@ -1,7 +1,6 @@
var WeatherForecast = (function() {
'use strict';
var weatherApiKey = 'Uxh4IdMuAvhSiBnsf4UUDVGF4e3YAp2B';
var weatherEnabled = false;
var weatherData = null;
var currentForecastIndex = 0;
@ -81,7 +80,6 @@ var WeatherForecast = (function() {
contentElement.innerHTML =
'<div class="weather-loading">' +
'<i class="layui-icon layui-icon-loading layui-icon-anim-rotate"></i>' +
'<p>请确保网络可访问 Windy API 服务</p>' +
'<p>正在获取天气预测数据...</p>' +
'</div>';
}
@ -140,23 +138,19 @@ var WeatherForecast = (function() {
}
function fetchWeatherData(lat, lon) {
var requestBody = {
"lat": parseFloat(lat.toFixed(2)),
"lon": parseFloat(lon.toFixed(2)),
"model": "gfs",
"parameters": ["temp", "wind", "precip", "pressure", "rh", "windGust"],
"levels": ["surface"],
"key": weatherApiKey,
"hours": 72
};
var url = 'https://api.open-meteo.com/v1/forecast?' +
'latitude=' + lat.toFixed(4) +
'&longitude=' + lon.toFixed(4) +
'&current=temperature_2m,wind_speed_10m,wind_direction_10m,relative_humidity_2m,surface_pressure' +
'&hourly=temperature_2m,relative_humidity_2m,wind_speed_10m,wind_direction_10m,precipitation,surface_pressure,wind_gusts_10m' +
'&forecast_days=3' +
'&timezone=auto';
fetch('https://api.windy.com/api/point-forecast/v2', {
method: 'POST',
fetch(url, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: JSON.stringify(requestBody)
}
})
.then(function(response) {
if (!response.ok) {
@ -165,7 +159,7 @@ var WeatherForecast = (function() {
return response.json();
})
.then(function(data) {
weatherData = data;
weatherData = transformOpenMeteoData(data);
var currentTime = new Date().getTime();
var closestIndex = 0;
@ -203,6 +197,40 @@ var WeatherForecast = (function() {
});
}
function transformOpenMeteoData(data) {
var transformed = {
ts: [],
'temp-surface': [],
'wind-speed': [],
'wind-direction': [],
'precipitation': [],
'rh-surface': [],
'pressure-surface': [],
'gust-surface': []
};
if (!data.hourly || !data.hourly.time) {
return transformed;
}
for (var i = 0; i < data.hourly.time.length; i++) {
transformed.ts.push(new Date(data.hourly.time[i]).getTime());
transformed['temp-surface'].push(data.hourly.temperature_2m[i]);
var windSpeedMs = data.hourly.wind_speed_10m[i] ? data.hourly.wind_speed_10m[i] / 3.6 : null;
transformed['wind-speed'].push(windSpeedMs);
transformed['wind-direction'].push(data.hourly.wind_direction_10m[i]);
transformed['precipitation'].push(data.hourly.precipitation[i]);
transformed['rh-surface'].push(data.hourly.relative_humidity_2m[i]);
transformed['pressure-surface'].push(data.hourly.surface_pressure[i]);
var gustMs = data.hourly.wind_gusts_10m[i] ? data.hourly.wind_gusts_10m[i] / 3.6 : null;
transformed['gust-surface'].push(gustMs);
}
return transformed;
}
function displayCurrentForecast() {
if (!weatherData || !weatherData.ts || weatherData.ts.length === 0) {
displayWeatherError('无可用的天气预测数据');
@ -213,22 +241,22 @@ var WeatherForecast = (function() {
var forecastHtml = '<div class="weather-forecast-item"><div class="weather-param-grid">';
if (weatherData['temp-surface'] && weatherData['temp-surface'][i] !== null) {
var temp = (weatherData['temp-surface'][i] - 273.15).toFixed(1);
var temp = weatherData['temp-surface'][i].toFixed(1);
forecastHtml += createWeatherParam('温度', temp + '°C');
}
if (weatherData['wind_u-surface'] && weatherData['wind_v-surface'] &&
weatherData['wind_u-surface'][i] !== null && weatherData['wind_v-surface'][i] !== null) {
var windU = weatherData['wind_u-surface'][i];
var windV = weatherData['wind_v-surface'][i];
var windSpeed = Math.sqrt(windU * windU + windV * windV).toFixed(1);
var windDir = getWindDirection(windU, windV);
if (weatherData['wind-speed'] && weatherData['wind-speed'][i] !== null) {
var windSpeed = weatherData['wind-speed'][i].toFixed(1);
forecastHtml += createWeatherParam('风速', windSpeed + ' m/s');
}
if (weatherData['wind-direction'] && weatherData['wind-direction'][i] !== null) {
var windDir = getWindDirectionFromDegrees(weatherData['wind-direction'][i]);
forecastHtml += createWeatherParam('风向', windDir);
}
if (weatherData['past3hprecip-surface'] && weatherData['past3hprecip-surface'][i] !== null) {
var precip = weatherData['past3hprecip-surface'][i].toFixed(1);
if (weatherData['precipitation'] && weatherData['precipitation'][i] !== null) {
var precip = weatherData['precipitation'][i].toFixed(1);
forecastHtml += createWeatherParam('降水', precip + ' mm');
}
@ -238,7 +266,7 @@ var WeatherForecast = (function() {
}
if (weatherData['pressure-surface'] && weatherData['pressure-surface'][i] !== null) {
var pressure = (weatherData['pressure-surface'][i] / 100).toFixed(0);
var pressure = weatherData['pressure-surface'][i].toFixed(0);
forecastHtml += createWeatherParam('气压', pressure + ' hPa');
}
@ -291,6 +319,14 @@ var WeatherForecast = (function() {
return directions[index];
}
function getWindDirectionFromDegrees(degrees) {
if (degrees === null || degrees === undefined) return '无风';
var directions = ['北', '东北', '东', '东南', '南', '西南', '西', '西北'];
var index = Math.round(degrees / 45) % 8;
return directions[index];
}
function isEnabled() {
return weatherEnabled;
}

View File

@ -851,7 +851,7 @@
<div id="weatherForecastCard" class="weather-forecast-card" style="display: none;" th:if="${role=='SUPER_ADMIN'}">
<div class="weather-card-header">
<h3 class="weather-card-title">
Windy 天气预测
Open-Meteo 天气预测
</h3>
<button class="weather-close-btn" onclick="closeWeatherCard()">×</button>
</div>
@ -948,10 +948,10 @@
</div>
<div class="dropdown-divider"></div>
<div class="dropdown-group" th:if="${role=='SUPER_ADMIN'}">
<div class="dropdown-group-title">Windy 天气预测</div>
<div class="dropdown-group-title">Open-Meteo 天气预测</div>
<div class="dropdown-item">
<label class="switch-label">
<span>启用天气预测(需网络被代理)</span>
<span>启用天气预测</span>
<input type="checkbox" id="enableWeatherSwitch" onchange="toggleWeatherForecast()">
<span class="switch-slider"></span>
</label>