7timer.info 免费天气预报对接记录

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.web.client.RestTemplate;

import java.time.*;
import java.time.format.DateTimeFormatter;

public class SevenTimerWeather {

    public static void main(String[] args) throws Exception {
        String url = "http://www.7timer.info/bin/astro.php"
                + "?lon=114.54503&lat=38.138258&ac=0&lang=en&unit=metric&output=json&tzshift=0";
        RestTemplate restTemplate = new RestTemplate();
        String json = restTemplate.getForObject(url, String.class);

        ObjectMapper mapper = new ObjectMapper();
        JsonNode root = mapper.readTree(json);

        // 起报时间(UTC)
        String initStr = root.get("init").asText();
        LocalDateTime initUtc = LocalDateTime.parse(initStr, DateTimeFormatter.ofPattern("yyyyMMddHH"));
        ZonedDateTime initBj = initUtc.atZone(ZoneOffset.UTC).withZoneSameInstant(ZoneId.of("Asia/Shanghai"));

        // 当前北京时间
        ZonedDateTime nowBj = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));

        // 选择最接近当前时间的预报
        JsonNode dataseries = root.get("dataseries");
        JsonNode bestNode = null;
        long bestDiff = Long.MAX_VALUE;

        for (JsonNode node : dataseries) {
            int tp = node.get("timepoint").asInt();
            ZonedDateTime forecastTime = initBj.plusHours(tp);
            long diff = Math.abs(Duration.between(forecastTime, nowBj).toMinutes());

            if (diff < bestDiff) {
                bestDiff = diff;
                bestNode = node;
            }
        }

        if (bestNode != null) {
            int temp = bestNode.get("temp2m").asInt();                        // 室外温度
            int rh = bestNode.get("rh2m").asInt();                             // 湿度%
            String windDir = bestNode.get("wind10m").get("direction").asText();
            int windSpeed = bestNode.get("wind10m").get("speed").asInt();
            int transparency = bestNode.get("transparency").asInt();

            double feelsLike = calcFeelsLike(temp, rh, windSpeed);
            String windDirCn = windDirToChinese(windDir);
            String visibility = transparencyToKm(transparency);

            ZonedDateTime forecastTime = initBj.plusHours(bestNode.get("timepoint").asInt());
            System.out.println("预报时间:" + forecastTime);
            System.out.println("室外温度:" + temp + " ℃");
            System.out.println("体感温度:" + String.format("%.1f ℃", feelsLike));
            System.out.println("风向:" + windDirCn);
            System.out.println("风速:" + windSpeed + " m/s");
            System.out.println("能见度:" + visibility);
            String weatherText = getWeatherText(
                    bestNode.get("cloudcover").asInt(),
                    bestNode.get("prec_type").asText()
            );

            System.out.println("天气:" + weatherText);
        }
    }

    // 根据 cloudcover 和 prec_type 推断天气文本
    private static String getWeatherText(int cloudcover, String precType) {
        if ("snow".equalsIgnoreCase(precType)) {
            if (cloudcover >= 7) return "大雪";
            else if (cloudcover >= 4) return "中雪";
            else return "小雪";
        } else if ("rain".equalsIgnoreCase(precType)) {
            if (cloudcover >= 7) return "大雨";
            else if (cloudcover >= 4) return "中雨";
            else return "小雨";
        } else { // 无降水
            if (cloudcover <= 2) return "晴";
            else if (cloudcover <= 5) return "少云/多云";
            else if (cloudcover <= 7) return "阴";
            else return "阴天";
        }
    }


    // 风向中文转换
    private static String windDirToChinese(String dir) {
        switch (dir) {
            case "N": return "北风";
            case "NE": return "东北风";
            case "E": return "东风";
            case "SE": return "东南风";
            case "S": return "南风";
            case "SW": return "西南风";
            case "W": return "西风";
            case "NW": return "西北风";
            default: return dir;
        }
    }

    // 透明度 -> 能见度公里数
    private static String transparencyToKm(int t) {
        switch (t) {
            case 1: return "≥20 km";
            case 2: return "16–20 km";
            case 3: return "12–16 km";
            case 4: return "8–12 km";
            case 5: return "4–8 km";
            case 6: return "2–4 km";
            case 7: return "1–2 km";
            case 8: return "0.5–1 km";
            case 9: return "<0.5 km";
            default: return "未知";
        }
    }

    // 简易体感温度计算
    private static double calcFeelsLike(double tempC, int humidity, double windMs) {
        if (tempC >= 27) {
            // 热指数公式 (近似)
            return -8.784695 + 1.61139411 * tempC + 2.338549 * humidity
                    - 0.14611605 * tempC * humidity
                    - 0.012308094 * tempC * tempC
                    - 0.016424828 * humidity * humidity
                    + 0.002211732 * tempC * tempC * humidity
                    + 0.00072546 * tempC * humidity * humidity
                    - 0.000003582 * tempC * tempC * humidity * humidity;
        } else if (tempC <= 10) {
            // 风寒指数公式
            double windKmh = windMs * 3.6;
            return 13.12 + 0.6215 * tempC - 11.37 * Math.pow(windKmh, 0.16)
                    + 0.3965 * tempC * Math.pow(windKmh, 0.16);
        } else {
            return tempC;
        }
    }
}
  • lat纬度 (Latitude)

    • 表示南北位置

    • 北纬为正,南纬为负

  • lon经度 (Longitude)

    • 表示东西位置

    • 东经为正,西经为负

坐标系使用WGS84格式,可以在谷歌地图拾取

posted @ 2025-09-26 11:29  Rolay  阅读(7)  评论(0)    收藏  举报