Open-Meteo 的气象数据主要来源于多个权威的 全球气象模型(Numerical Weather Prediction, NWP) 和 卫星/地面观测数据,通过数据融合和后处理提供高精度的免费气象 API。
Open-Meteo与其他服务的对比
数据特性 | Open-Meteo | OpenWeatherMap | WeatherAPI |
---|---|---|---|
免费层限制 | 较宽松 | 严格(1000次/天) | 中等(50万次/月) |
数据来源 | ECMWF/GFS 为主 | GFS/私有数据 | 多源混合 |
历史数据 | 支持(ERA5) | 需付费 | 有限支持 |
更新频率 | 每小时(短期) | 每 2 小时 | 每 1-3 小时 |
代码实现:
public static void main(String[] args) {
List<HistoricalWeather> history = getHistory("经度", "维度",
LocalDate.of(2024, 1, 1), LocalDate.of(2024, 12, 31));
history.sort(Comparator.comparing(HistoricalWeather::getDate));
history.forEach(day -> System.out.printf(
"%s | 最大风速: %.1fm/s | 风向: %.0f° | 平均温度: %.1f℃ | 海拔: %.0f米 | 气压: %.0f hPa\n",
day.getDate(), day.getWindSpeed(), day.getWindDirection(),
day.getTemperature(), day.getElevation(),day.getPressure()
));
}
/**
* 获取历史气象数据
* @param lat 纬度
* @param lon 经度
* @param start 开始日期
* @param end 结束日期
*/
public static List<HistoricalWeather> getHistory(double lat, double lon, LocalDate start, LocalDate end) throws IOException {
// 1. 获取海拔
Double elevation = getElevation(lat, lon);
// 2. 获取历史气象数据
String url = String.format("%s?latitude=%.4f&longitude=%.4f&start_date=%s&end_date=%s" +
"&hourly=windspeed_10m,winddirection_10m,temperature_2m,surface_pressure",
API_URL, lat, lon, start, end);
JsonNode response = callApi(url);
// 3. 解析数据(以每日平均为例)
return parseHourlyToDaily(response, elevation);
}
private static Double getElevation(double lat, double lon) throws IOException {
String url = String.format("%s?latitude=%.4f&longitude=%.4f", ELEVATION_API, lat, lon);
JsonNode response = callApi(url);
return response.path("elevation").get(0).asDouble();
}
private static List<HistoricalWeather> parseHourlyToDaily(JsonNode response, Double elevation) {
List<HistoricalWeather> result = new ArrayList<>();
JsonNode hourly = response.path("hourly");
//时间
List<String> times = mapper.convertValue(hourly.path("time"), new TypeReference<List<String>>(){});
//风速
List<Double> windSpeeds = mapper.convertValue(hourly.path("windspeed_10m"), new TypeReference<List<Double>>(){});
//风向
List<Double> windDirs = mapper.convertValue(hourly.path("winddirection_10m"), new TypeReference<List<Double>>(){});
//温度
List<Double> temps = mapper.convertValue(hourly.path("temperature_2m"), new TypeReference<List<Double>>(){});
//气压
List<Double> pressure = mapper.convertValue(hourly.path("surface_pressure"), new TypeReference<List<Double>>(){});
// 按日期聚合(计算日均)
Map<LocalDate, List<Double>> dailyWindSpeed = new HashMap<>();
Map<LocalDate, List<Double>> dailyWindDir = new HashMap<>();
Map<LocalDate, List<Double>> dailyTemp = new HashMap<>();
Map<LocalDate, List<Double>> pressureMap = new HashMap<>();
for (int i = 0; i < times.size(); i++) {
LocalDateTime time = LocalDateTime.parse(times.get(i));
LocalDate date = time.toLocalDate();
dailyWindSpeed.computeIfAbsent(date, k -> new ArrayList<>()).add(windSpeeds.get(i));
dailyWindDir.computeIfAbsent(date, k -> new ArrayList<>()).add(windDirs.get(i));
dailyTemp.computeIfAbsent(date, k -> new ArrayList<>()).add(temps.get(i));
pressureMap.computeIfAbsent(date, k -> new ArrayList<>()).add(pressure.get(i));
}
// 计算日均值
for (LocalDate date : dailyWindSpeed.keySet()) {
HistoricalWeather day = new HistoricalWeather();
day.setDate(date);
day.setElevation(elevation);
day.setWindSpeed(dailyWindSpeed.get(date).stream().mapToDouble(Double::doubleValue).average().orElse(0));
day.setWindDirection(dailyWindDir.get(date).stream().mapToDouble(Double::doubleValue).average().orElse(0));
day.setTemperature(dailyTemp.get(date).stream().mapToDouble(Double::doubleValue).average().orElse(0));
day.setPressure(pressureMap.get(date).stream().mapToDouble(Double::doubleValue).average().orElse(0));
result.add(day);
}
return result;
}
private static JsonNode callApi(String url) throws IOException {
try (CloseableHttpClient client = HttpClients.createDefault()) {
HttpGet request = new HttpGet(url);
try (CloseableHttpResponse response = client.execute(request)) {
return mapper.readTree(EntityUtils.toString(response.getEntity()));
}
}
}
@Data
public class HistoricalWeather {
private LocalDate date;
private Double windSpeed; // m/s
private Double windDirection; // 角度 (0-360°)
private Double elevation; // 米
private Double temperature; // ℃
private Double pressure;
}
这里是通过经纬度以及时间作为条件来获取历史气象数据,主要包括风速,风向,温度,气压等信息的平均值
控制台打印如下