使用Open-Meteo 来获取历史天气数据(根据经纬度)

Open-Meteo 的气象数据主要来源于多个权威的 全球气象模型(Numerical Weather Prediction, NWP) 和 卫星/地面观测数据,通过数据融合和后处理提供高精度的免费气象 API。

Open-Meteo与其他服务的对比

数据特性Open-MeteoOpenWeatherMapWeatherAPI
免费层限制较宽松严格(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;
}

 

这里是通过经纬度以及时间作为条件来获取历史气象数据,主要包括风速,风向,温度,气压等信息的平均值

控制台打印如下

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值