绘制台风路径(二)

 python爬取台风路径

# 导入所需模块
import requests
import json
import re
import time
import pandas as pd
import datetime

# 设置请求头,模仿浏览器行为
headers = {'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'}

# 定义函数来显示某一年的所有台风编号和名称
def show_tc_nums_and_names_by_year(year):
    # 构建URL以获取JSON数据
    url = f"http://typhoon.nmc.cn/weatherservice/typhoon/jsons/list_{year}?callback=typhoon_jsons_list_{year}"
    # 发送GET请求并获取响应文本
    html_obj = requests.get(url, headers=headers).text
    
    # 处理字符串以提取有用的JSON部分
    json_obj = html_obj[html_obj.index("(") + 1:html_obj.rindex(")")]
    # 将JSON字符串转换为字典
    json_dict = json.loads(json_obj)
    
    # 解析JSON数据中的台风编号和名称
    typhoon_list = json_dict.get("typhoonList", [])
    if typhoon_list:
        print(f"Year {year} has the following typhoons:")  # 打印年份和台风信息提示
        for typhoon in typhoon_list:
            # 格式化台风编号
            num = f"TC{typhoon[3]}"
            # 获取或生成中文名称
            name_cn = typhoon[2] if typhoon[2] != "null" else ""
            # 获取英文名称
            name_en = typhoon[1]
            # 打印台风编号及名称
            print(f"{num} ({name_en}, {name_cn})")
    else:
        print(f"No typhoon information found for year {year}")  # 如果没有找到台风信息,则打印提示

# 定义函数预测给定日期加上小时增量后的日期
def date_pred(date, deltahour):
    # 解析日期字符串为datetime对象
    time = datetime.datetime.strptime(date, "%Y%m%d%H%M")
    # 计算新日期
    new_date = (time + datetime.timedelta(hours=deltahour)).strftime("%Y%m%d%H%M")
    # 返回新日期字符串
    return new_date

# 定义函数根据类型字符串返回台风强度描述
def get_type(date_type):
    # 定义类型映射
    item = {'TC': '热带气旋', 'TD': '热带低压', 'TS': '热带风暴', 'STS': '强热带风暴',
            'TY': '台风', 'STY': '强台风', 'SuperTY': '超强台风', '': '',}
    # 返回对应描述或默认值
    return item.get(date_type, '')

# 定义函数获取特定台风的详细信息
def get_tc_info(item):
    # 创建13位时间戳
    t = int(round(time.time() * 1000))
    # 构建URL并发送GET请求
    url = 'http://typhoon.nmc.cn/weatherservice/typhoon/jsons/view_%s?t=%s&callback=typhoon_jsons_view_%s' % (item['id'], t, item['id'])
    html_obj = requests.get(url, headers=headers, verify=False).text
    # 提取JSON数据
    data = json.loads(re.match(".*?({.*}).*", html_obj, re.S).group(1))['typhoon']

    # 创建字典存储台风信息
    info_dicts = { 
        'tc_num':item['tc_num'],  # 台风编号
        'name_cn':item['name_cn'], # 中文名称
        'name_en':item['name_en'], # 英文名称
        'dateUTC':[],    # UTC日期
        'dateCST':[],    # CST日期
        'vmax':[],    # 最大风速 m/s
        'grade':[],   # 强度等级
        'lat':[],   # 纬度
        'lon':[],   # 经度
        'mslp':[],    # 中心气压 hPa
        'attr':[]   # 属性:预报或分析
    }

    # 遍历分析数据
    for v in data[8]:
        info_dicts['dateUTC'].append(v[1])
        info_dicts['dateCST'].append(date_pred(v[1], 8))  # 将UTC时间转换为CST时间
        info_dicts['vmax'].append(v[7])
        info_dicts['grade'].append(get_type(v[3]))
        info_dicts['lon'].append(v[4])
        info_dicts['lat'].append(v[5])
        info_dicts['mslp'].append(v[6])
        info_dicts['attr'].append('analysis')

    # 获取最新预报时间
    dateUTC0 = info_dicts['dateUTC'][-1]

    # 处理最新预报
    if len(data[8][-1]) > 11 and data[8][-1][11] is not None and 'BABJ' in data[8][-1][11]:
        BABJ_list = data[8][-1][11]['BABJ']
        for i in range(len(BABJ_list)):
            # 获取预报前置时间(小时)
            pred_hour = int(BABJ_list[i][0])
            # 预测UTC时间
            dateUTC_pred = date_pred(dateUTC0, pred_hour)
            info_dicts['dateUTC'].append(dateUTC_pred)
            info_dicts['dateCST'].append(date_pred(dateUTC_pred, 8))
            info_dicts['vmax'].append(BABJ_list[i][5])
            info_dicts['grade'].append(get_type(BABJ_list[i][7]))
            info_dicts['lon'].append(BABJ_list[i][2])
            info_dicts['lat'].append(BABJ_list[i][3])
            info_dicts['mslp'].append(BABJ_list[i][4])
            info_dicts['attr'].append('forecast')

    # 将字典转换为DataFrame
    tc_info = pd.DataFrame(info_dicts)
    # 返回DataFrame
    return tc_info

# 定义函数根据年份和编号获取台风信息
def get_tc_info_by_year_and_num(year, num):
    # 构建URL并发送GET请求
    url = f"http://typhoon.nmc.cn/weatherservice/typhoon/jsons/list_{year}?callback=typhoon_jsons_list_{year}"
    
    html_obj = requests.get(url, headers=headers, verify=False).text
    # 打印响应文本(调试用途)
    print(html_obj)
    # 提取JSON数据
    data = json.loads(re.match(".*?({.*}).*", html_obj, re.S).group(1))

    # 检查是否存在台风列表
    if data.get('typhoonList'):
        for item in data['typhoonList']:
            # 如果找到了匹配的台风编号
            if item[4] == num:
                # 创建包含台风ID、编号、中文名和英文名的字典
                tc = {
                    'id': item[0],
                    'tc_num': str(item[4]),
                    'name_cn': item[2],
                    'name_en': item[1]
                }
                # 返回台风详细信息
                return get_tc_info(tc)
    # 如果没有找到匹配的台风,则返回None
    return None

# 主程序入口
if __name__ == "__main__":
    # 设置请求头(重复,应避免)
    headers = {'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'}

    # 输入年份
    year = input("请输入年份: ")
    # 显示该年份的台风编号和名称
    show_tc_nums_and_names_by_year(year)
    # 输入台风TC编号(四位数字)
    num = input("请输入台风TC编号(TC后面四位数字): ")

    # 获取台风信息
    data = get_tc_info_by_year_and_num(year, num)

    # 如果数据存在,则打印并保存到CSV和Excel文件
    if data is not None:
        print(data)
        data.to_csv(rf'{year}_TC{num}.csv', index=False)
        print(f"台风{num}的数据已保存到文件{year}_TC{num}.csv")
        data.to_excel(rf'{year}_TY{num}.xlsx', index=False, engine='openpyxl')
        print(f"台风{num}的数据已保存到文件{year}_TC{num}.xlsx")
    # 如果未找到数据,则打印提示
    else:
        print(f"在{year}年未找到台风{num}的数据。")

 绘制路径图

import os  
import pandas as pd
import geopandas as gpd  
import numpy as np  
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['Microsoft YaHei']  # 指定默认字体  
plt.rcParams['axes.unicode_minus'] = False  # 解决保存图像时负号'-'显示为方块的问题
# 设定默认字体大小为12  
plt.rcParams['font.size'] = 12   
import cartopy.crs as ccrs  
import cartopy.feature as cfeat
from matplotlib.lines import Line2D    
from matplotlib.collections import LineCollection  
from cartopy.mpl.ticker import LongitudeFormatter, LatitudeFormatter  
  
# 读取数据  
df = pd.read_csv('D:/data/2024_TY2403.csv')  
  
# 假设正确的列名是 'grade' 而不是 'grade_at_point',并且它表示每个点的强度级别  
# 如果不是,请替换为正确的列名  
  
# 定义颜色函数  
def get_color(grade):  
    if grade == '热带低压' or grade == '热带扰动':  
        return '#FFFF00'  
    elif grade == '热带风暴':  
        return '#6495ED'  
    elif grade == '强热带风暴':  
        return '#3CB371'  
    elif grade == '台风':  
        return '#FFA500'  
    elif grade == '强台风':  
        return '#FF00FF'  
    elif grade == '超强台风':  
        return '#DC143C'  
# 创建地图并绘制台风路径  
def plot_typhoons(df, extent):
    gdf = gpd.read_file('D:/data/中国_省.shp') 
    fig = plt.figure(figsize=(12, 8))  
    ax = fig.add_subplot(1, 1, 1, projection=ccrs.PlateCarree())  
    
    # 添加底图  
    ax.add_feature(cfeat.COASTLINE)  
    # 添加中国省界  
    gdf.boundary.plot(ax=ax, transform=ccrs.PlateCarree(), linewidth=0.5, edgecolor='black')
    
    # 设置地图范围  
    ax.set_extent(extent, crs=ccrs.PlateCarree())  
    
    # 添加网格线  
    gl = ax.gridlines(draw_labels=True, linewidth=1, color='gray', alpha=0.5, linestyle='--')  
    gl.xlabels_top = gl.ylabels_right = False  
    
    # 设置经纬度格式和刻度  
    ax.xaxis.set_major_formatter(LongitudeFormatter())  
    ax.yaxis.set_major_formatter(LatitudeFormatter()) 
    
    # 绘制台风路径  
    for tc_num, group in df.groupby('tc_num'):  
        points = np.array(list(zip(group['lon'], group['lat'])))  
        if 'grade' in group.columns:  # 确保 'grade' 列存在  
            colors = [get_color(grade) for grade in group['grade']]  # 使用 'grade' 列  
        else:  
            # 如果没有 'grade' 列,可以使用默认颜色或其他逻辑  
            colors = ['#000000'] * len(points)  # 例如,使用黑色  
  
        segments = np.array([points[i:i+2] for i in range(len(points)-1)])  
        lc = LineCollection(segments, colors=colors, linewidths=2)  
        ax.add_collection(lc) 
        # 添加图例(这里需要手动指定,因为使用了循环绘制)  
        legend_elements = [  
        Line2D([], [], color='#FFFF00', marker='o', label='热带低压/扰动', markersize=5),  
        Line2D([], [], color='#6495ED', marker='o', label='热带风暴', markersize=5),  
        Line2D([], [], color='#3CB371', marker='o', label='强热带风暴', markersize=5),  
        Line2D([], [], color='#FFA500', marker='o', label='台风', markersize=5),  
        Line2D([], [], color='#FF00FF', marker='o', label='强台风', markersize=5),  
        Line2D([], [], color='#DC143C', marker='o', label='超强台风', markersize=5),  
    ]  
        ax.legend(handles=legend_elements, loc='upper left')  
      
    # 设置标题  
    plt.title('2403台风格美路径')  
  
    # 显示图形  
    plt.show()  
  
# 调用函数,替换 extent 为所需的经纬度范围  
plot_typhoons(df, extent=[100, 150, 0, 40])  # 根据需要设置extent

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值