简介这份1950—2018年中国台风Shapefile数据集面向气象研究、GIS空间分析与灾害风险管理人员提供中国沿海台风历史记录的点、线两类图层可支撑台风频率与强度统计、登陆地点分布、路径特征以及时间空间关联等分析。压缩包共10个文件约1005KB内含shp主数据、dbf属性表、shx索引并附prj投影与xml元数据结构完整能在ArcGIS、QGIS等常见GIS软件中直接打开、配图与二次计算。已有1785人学习下载在教学实验和科研入门场景中具有一定参考价值。数据以点图层记录台风位置、线图层刻画移动轨迹便于复现历史台风过程、制作专题地图并为风暴潮模拟、城市防灾规划与高风险区域识别提供基础底图和分析依据结合属性表还可围绕台风等级、登陆地点和移动路径开展统计适合课程设计、论文前期探索及课题数据准备使用。1. 解压后先别拖进 QGIS1950-2018 中国台风 shp 到底能干什么解压之后是几个.shp、.dbf、.shx外加一个.prj目录名写着 1950-2018。很多人的第一反应是把 shp 拖进 QGIS屏幕上瞬间糊上一层密密麻麻的点然后不知道下一步该点哪里。这份中国台风 shp 数据本质上是 68 年西北太平洋热带气旋最佳路径的点集每个点记录某一时刻台风中心的位置、中心气压、最大风速和强度等级按台风编号可以串成一条完整轨迹。它能解决的问题很具体做沿海灾害风险评估、做风场重现期统计、做登陆路径的时空分布、给某个工程选址找历史影响样本。适合气象、海洋、防灾、GIS 方向做空间分析的人也适合需要一份可复现历史台风底图的学生。但它的坐标系、字段编码和时间格式都有历史包袱直接出图大概率会翻车。2. 坐标系先对齐WGS84 经纬度、投影与距离量算的三层选择2.1 为什么不能拿到点就直接量距离台风 shp 里的经度、纬度字段通常是以度为单位的浮点数.prj文件大概率写着GCS_WGS_1984。这意味着图层是地理坐标系不是投影坐标系。地理坐标系下QGIS 或 ArcGIS 的测量工具算出来的“距离”是按椭球面或者球面近似算的单位是度不是米。如果你在属性表里直接用$length或者字段计算器算两点距离得到的小数可能在 0.5 到 3 之间看起来像“公里”实际上是“度”拿去和缓冲区半径、行政区划面积做叠加结果全错。常见做法是先把点图层投影到等距或等积坐标系再量算而不是在 WGS84 经纬度上直接算。台风路径的跨度大从南海到西北太平洋单个台风的轨迹可能跨十几个经度用全国 Albers 投影或者东亚 Albers 投影比较稳如果只做局地缓冲分析比如某个沿海城市 100 公里范围内的台风点用该城市所在的 UTM 分带也可以但要接受跨带拼接时的接缝误差。我一般会备两个图层一个保留 WGS84 原始经纬度用于出图和交汇一个投影到 Albers 用于距离、面积和缓冲区计算需要的时候再 Join 回去。这里有一个反直觉的边界WGS84 和 CGCS2000 在台风尺度上的差异通常只有厘米级到分米级对 68 年历史路径的统计分析影响很小。真正让结果翻车的不是基准面而是“把度当米”和“投影参数写错中央经线”。2.2 在 QGIS 里确认与转换坐标系拿到数据后先做三件事看.prj、看图层属性、看坐标显示。QGIS 里右键图层 → 属性 → 信息能看到图层声明的 CRS。如果.prj缺失或者写着Unknown不要猜打开属性表看经纬度数值范围中国近海台风点的经度大致在 100 到 180 之间纬度在 0 到 45 之间出现这种范围基本可以判定是经纬度。此时用“图层 → 设置图层 CRS”指定 EPSG:4326再另存一份避免后续误操作覆盖原文件。投影转换建议走“另存为”而不是改原图层。步骤是右键 → 导出 → 要素另存为CRS 选择EPSG:4326存一份原始版再选目标投影存一份分析版。目标投影可以用自定义 Albers参数按中国区域设置参数建议值说明投影方法Albers Equal Area Conic等积适合面积统计中央经线105覆盖中国及近海标准纬线 125南方标准纬线 247北方原点纬度0不影响变形分布如果只是做路径长度和缓冲区等距投影Equidistant Conic也可以如果要做风圈面积、影响范围面积优先等积投影。选错投影不会报错只会让面积和距离悄悄偏几个百分点这种偏差在单条路径上看不出来在 68 年统计里会放大。2.3 Python 读入后的坐标系处理用geopandas读 shp 时默认会读取.prj里的 CRS。如果.prj缺失gdf.crs返回None后续做to_crs会直接抛异常。稳妥的写法是先判断、再补定义、最后投影import geopandas as gpd # 读取台风点 shp路径按实际解压位置替换 gdf gpd.read_file(typhoon_points.shp) # 如果 .prj 缺失手动补定义 WGS84 经纬度 if gdf.crs is None: gdf gdf.set_crs(epsg4326) # 统一投影到 Albers便于后续距离和面积计算 albers projaea lat_125 lat_247 lat_00 lon_0105 datumWGS84 unitsm no_defs gdf_proj gdf.to_crs(albers) # 检查投影后的坐标量级应该在几十万到几百万米 print(gdf_proj.geometry.x.head()) print(gdf_proj.crs)这段代码的关键点在set_crs和to_crs的顺序set_crs只声明“这些数值是什么坐标系”不做坐标变换to_crs才真正做数学转换。如果源数据本来就是经纬度但.prj写错先set_crs(4326)再to_crs是安全路径。参数里lat_1、lat_2是标准纬线lon_0是中央经线unitsm决定输出单位。改中央经线会整体平移坐标但不改变相对位置关系标准纬线选得离研究区越近变形越小。2.4 投影选择的边界不是所有分析都必须投影。做轨迹出图、做点与省界叠加、做按经纬度网格统计保留 WGS84 反而更省事因为底图、边界、影像大多是经纬度。只有在涉及“距离多少公里”“缓冲区半径多少米”“面积多少平方公里”时才必须投影。我一般把这条规则写在流程最前面先问输出物是图还是量算值再决定用哪个图层。混用两个图层做空间连接时QGIS 会自动做动态投影但字段计算器里的$length、$area仍然按图层自身 CRS 算不会自动帮你换单位这是最容易忽略的一层。3. 属性表拆解编号、时间、风速、气压字段怎么读才不误判3.1 字段清单与单位约定打开.dbf属性表不同来源的字段名会有差异但核心字段基本围绕“谁、什么时候、在哪、多强”四件事。下面这张表是我处理这类数据时固定先核对的一版字段名以实际 dbf 为准含义和单位是通用的字段常见写法含义单位/格式注意点TCID / ID台风编号6 位或 8 位整数年份 序号跨年要拆NAME台风名称字符串早期记录可能为空TIME / YYYYMMDDHH观测时间10 位整数缺小时位会误判LAT中心纬度度0.1 精度北纬为正LON中心经度度0.1 精度东经为正PRES中心气压hPa缺测常见 -999WND最大风速m/s与风力等级不是一回事GRADE / INTENSITY强度等级整数或字符串分级标准要统一MOVDIR移动方向度或方位部分年份缺失MOVSPD移动速度km/h与路径长度不能混算先用 QGIS 的“打开属性表 → 字段统计”看一遍每个数值字段的最小值、最大值和空值数量。PRES出现 -999 或 0WND出现 -999基本就是缺测标记。LAT、LON出现 0 或者超出中国近海范围也要单独拎出来看有可能是录入占位。3.2 台风编号的年份规则与跨年问题编号通常前四位是年份、后两位是当年序号比如 195001 表示 1950 年第 1 号台风。问题在于一个台风可能跨年12 月底生成、次年 1 月消散编号仍归生成年但轨迹点的时间已经进入下一年。如果你直接按TIME字段的年份分组统计“每年台风数量”跨年台风会被拆成两年数量对不上。正确做法是按TCID去重后再按编号前四位统计生成年而不是按观测时间年份统计。另一个坑是早期编号位数不统一。有的年份用 4 位有的用 6 位混在一起按字符串排序会出现 19501 排在 195001 后面的情况。处理时统一转成整数或补零到 6 位再排序。用字段计算器可以这样补-- QGIS 字段计算器新建整数字段 yyyy提取生成年 to_int(substr(lpad(to_string(TCID), 6, 0), 1, 4))lpad把编号补到 6 位substr取前 4 位to_int转整数。这样跨年台风不会因为观测时间被拆开后续按yyyy分组统计的才是“生成年台风数”。3.3 用字段计算器生成可用的时间字段TIME如果是 10 位整数QGIS 不会自动识别成日期时间直接做时间轴动画会失败。需要新建一个字符串或日期时间字段。简单做法是拆成日期和时间两列再拼成 ISO 格式-- 新建 date_str 字段输出 YYYY-MM-DD substr(lpad(to_string(TIME), 10, 0), 1, 4) || - || substr(lpad(to_string(TIME), 10, 0), 5, 2) || - || substr(lpad(to_string(TIME), 10, 0), 7, 2) -- 新建 datetime_str 字段输出 YYYY-MM-DD HH:MM substr(lpad(to_string(TIME), 10, 0), 1, 4) || - || substr(lpad(to_string(TIME), 10, 0), 5, 2) || - || substr(lpad(to_string(TIME), 10, 0), 7, 2) || || substr(lpad(to_string(TIME), 10, 0), 9, 2) || :00lpad的作用是防止早期记录只有 8 位缺小时时substr取错位置。补到 10 位后再切片前 4 位年、5-6 位月、7-8 位日、9-10 位小时。生成datetime_str之后在图层属性 → 时间设置里选这个字段时间轴才能按小时步进。如果只做年际统计date_str就够了不必上时间轴。3.4 缺测值与强度等级的判定强度等级字段最容易出问题。不同年代、不同来源的分级口径不完全一致有的按 2 分钟平均风速分热带低压、热带风暴、强热带风暴、台风、强台风、超强台风有的只给一个整数编码。直接拿来做颜色分级会出现同一颜色对应两个等级、或者图例顺序错乱的情况。我的处理方式是回到风速字段自己重算一套分级而不是完全信任GRADE。用WND按阈值划分阈值按当前采用的标准固定下来写进字段计算器或者 Python 脚本保证 68 年用的是同一套尺子import pandas as pd # 读取 dbf 转成的 csv 或直接读属性表导出 df pd.read_csv(typhoon_attr.csv) # 统一缺测标记风速 -999 视为无效 df.loc[df[WND] 0, WND] None # 按最大风速划分强度阈值仅作示例按采用标准调整 def grade(w): if pd.isna(w): return 缺测 if w 17.2: return 热带低压 if w 24.5: return 热带风暴 if w 32.7: return 强热带风暴 if w 41.5: return 台风 if w 51.0: return 强台风 return 超强台风 df[GRADE_RECALC] df[WND].apply(grade) print(df[GRADE_RECALC].value_counts())阈值数字不是重点重点是全表用同一套。WND 0先置空避免 -999 被当成超强台风。重算之后拿GRADE_RECALC和原始GRADE做交叉表差异大的年份单独标出来通常是早期记录或转换环节的问题不要直接删保留标记以便追溯。4. 把散点连成轨迹QGIS 点集转线与 Python 分组建线4.1 点集转线的分组与排序逻辑点集转线的核心不是“转”而是“按什么分组、按什么排序”。分组字段必须是台风编号TCID排序字段必须是时间TIME两个条件缺一不可。如果只按编号分组不排序QGIS 会按要素在 dbf 中的物理顺序连线早期数据按年份批量入库同一台风的点大概率连续但跨年补录或人工调整过的记录会打乱顺序线就会来回打结。如果只排序不分组所有台风会被连成一条从 1950 年到 2018 年的巨线。QGIS 的“点集转线”工具Points to Line有两个关键参数Order by和Group by。Order by选TIME升序Group by选TCID。线图层生成后属性表里每个TCID对应一条或多条线要素如果某个台风中间有缺测点被跳过可能断成多段这是正常的不要强行合并。4.2 QGIS 图形界面操作步骤第一步确认点图层已经按TCID和TIME排好序。可以在属性表里点两次列头看排序是否稳定。第二步打开处理工具箱搜索“点集转线”。第三步输入图层选台风点Order by选TIMEGroup by选TCID输出保存为typhoon_tracks.gpkg或 shp。第四步生成后打开线图层属性表检查要素数量是否和唯一TCID数量大致对应。如果线要素数远大于编号数说明分组字段有重复或空值如果远小于说明分组字段选错可能把所有点连成了一条。线生成后不要急着做符号化先做一个自检随便挑一个TCID用“按表达式选择”选出该编号的所有点再看对应线是否穿过这些点且顺序正确。跨年台风重点看时间排序如果按字符串而不是数值1950123123和1951010100的比较可能出错因为字符串逐位比较在月份进位时会翻车。TIME是 10 位定长整数时字符串排序通常没问题但补零不完整时就会出岔子。4.3 Python geopandas 批量生成轨迹线图形界面适合抽查批量生成和复现建议走 Python。思路是按TCID分组、组内按TIME排序、把点坐标串成LineString再拼回 GeoDataFrameimport geopandas as gpd import pandas as pd from shapely.geometry import LineString # 读取点图层保留原始坐标系 pts gpd.read_file(typhoon_points.shp) # 统一编号和时间类型防止字符串排序错乱 pts[TCID] pts[TCID].astype(int) pts[TIME] pts[TIME].astype(int) # 按台风编号分组组内按时间升序 lines [] for tcid, group in pts.sort_values([TCID, TIME]).groupby(TCID): # 少于两个点的组无法成线跳过 if len(group) 2: continue coords [(x, y) for x, y in zip(group.geometry.x, group.geometry.y)] lines.append({ TCID: tcid, start_time: group[TIME].min(), end_time: group[TIME].max(), n_points: len(group), geometry: LineString(coords) }) track gpd.GeoDataFrame(lines, crspts.crs) # 输出为 gpkg避免 shp 字段名截断 track.to_file(typhoon_tracks.gpkg, layertracks, driverGPKG)这段代码里sort_values([TCID, TIME])是关键先按编号再按时间保证每个组内点序正确。len(group) 2跳过单点台风避免LineString抛异常。n_points字段是后续自检用的线要素的n_points应该等于该编号的点数如果不相等说明分组或去重环节有问题。输出用 GPKG 而不是 shp是因为 shp 的 dbf 字段名最多 10 个字符start_time、end_time会被截断成start_time和end_time还能凑合但更长字段名会直接丢失GPKG 没有这个限制。4.4 线结果的自检线生成后做三个检查一是线要素数量与唯一TCID数量对比差值应该只来自单点台风二是随机抽 5 条线量算首尾点距离和属性表里首尾时间差乘以平均移动速度做粗估数量级对不上就回去查排序三是把线叠加到点图层上看线是否严格穿过每个点如果线跳过某些点说明生成线的输入不是完整点集可能被提前过滤过。还有一个容易忽略的细节经度跨越 180 度线。西北太平洋台风有一部分会越过 180 度经线进入西半球经纬度从 179.9 跳到 -179.9直接连 LineString 会在图上拉出一条横穿整个地图的假线。处理方式是在生成线之前把经度统一到 0-360 或者对跨越点做拆分不要等到出图才发现。5. 避坑排查六个让台风轨迹出错的细节5.1 现象缓冲区半径明明设了 100 公里结果圈住半个省原因点图层还是 WGS84 经纬度缓冲区工具按“度”计算100 度直接把整个研究区包进去。QGIS 虽然会弹窗提示图层地理坐标系可能影响距离计算但很多人点“仍要继续”。解决先把点图层to_crs到 Albers 或当地 UTM再执行缓冲区或者在缓冲区工具里勾选“动态投影到等距坐标系”但不要依赖默认行为。做完缓冲区后用测量工具量一下半径是否接近 100000 米而不是 100。5.2 现象属性表里字段名变成TCID_1、WND_2中文字段全是问号原因shp 的 dbf 字段名限制 10 个字符超出会被截断重名字段自动加后缀dbf 编码如果不是 UTF-8QGIS 按系统编码读会乱码。早期数据用 GBK 或 Latin-1 存储很常见。解决先用“图层属性 → 源 → 编码”试GBK和UTF-8看哪个能正常显示中文确认后另存为 GPKG字段名不再截断。如果必须交 shp把字段名提前改成 10 字符以内的英文缩写并在文档里写映射表不要指望接收方猜。5.3 现象轨迹线在图上反复折返像一团乱麻原因点集转线时只按TCID分组没有按TIME排序或者TIME字段是字符串且补零不完整排序结果不是时间顺序。解决把TIME转成整数或标准日期时间再排序生成线之前先在属性表里按TCID、TIME双列排序肉眼抽查一个台风的点序是否从生成到消散单调推进。Python 脚本里sort_values([TCID, TIME])不能省。5.4 现象轨迹线中间有一段直线横穿几百公里明显不合理原因中间有缺测点被跳过前后两个有效点直接相连形成一条“跳点直线”。也可能是经度跨越 180 度线导致坐标跳变。解决在生成线之前统计每个TCID相邻点的时间差超过 6 小时或 12 小时的段落标记出来决定是断开还是插值。缺测点不要用 0 填充0 会被当成有效坐标连到赤道上。跨越 180 度线的台风单独处理经度统一到 0-360 再连线。5.5 现象同一张图里 1950 年代的台风颜色和 2010 年代对不上原因强度分级字段GRADE在不同年代用了不同标准或者早期记录直接缺GRADE符号化时被归到默认类别。解决不要直接用原始GRADE做唯一值符号化回到WND按统一阈值重算分级缺测单独设灰色。重算后的分级字段写回图层图例按固定顺序排列保证跨年份可比。5.6 现象按年份统计台风数量某一年突然翻倍原因跨年台风按观测时间年份被拆到两年或者编号补零不一致导致同一台风出现两个编号。解决按TCID去重后取编号前四位作为生成年不要按TIME年份统计。补零统一到 6 位再转整数检查是否有19501和195001并存的情况。6. 进阶验证缓冲区叠加与年际统计校验数据可信度数据能不能用不看它画出来好不好看看它能不能通过两道校验空间上和历史记录对得上时间上统计口径自洽。第一道是缓冲区叠加。把轨迹线投影到 Albers 后对沿海某行政区做 200 公里缓冲再与台风点做空间连接统计每年落入缓冲区内的台风编号数量。这个数字不需要和官方发布完全一致但量级和趋势要合理比如 1950 年代观测记录偏少数量应该明显低于 1990 年代以后如果反过来了大概率是早期数据缺测被当成有效点或者缓冲区单位又搞错了。import geopandas as gpd # 读取投影后的轨迹线、沿海行政区面 tracks gpd.read_file(typhoon_tracks.gpkg, layertracks) coast gpd.read_file(coast_admin.gpkg) # 行政区做 200 公里缓冲单位米 coast_buf coast.copy() coast_buf[geometry] coast_buf.geometry.buffer(200000) # 空间相交统计落入缓冲区内的台风编号 hit gpd.sjoin(tracks, coast_buf, predicateintersects) hit[year] hit[TCID].astype(str).str.zfill(6).str[:4].astype(int) yearly hit.groupby(year)[TCID].nunique() print(yearly.tail(20))buffer(200000)的单位是米前提是coast已经投影如果面图层还是经纬度这里会得到 200000 度的缓冲直接报错或者生成一个覆盖全球的面。nunique()按编号去重避免一个台风多条线被重复计数。输出结果拿去做年际折线看 1970 年代前后是否有明显的观测能力跃升这通常是数据本身的历史特征不是分析错误。第二道是轨迹长度与移动速度的交叉校验。用线长度除以首尾时间差得到平均移动速度和属性表里的MOVSPD均值对比偏差超过一倍就抽查具体台风。台风平均移动速度通常在 15 到 30 公里/小时出现 200 公里/小时的多半是跳点直线或者经度跨越 180 度没有处理。这个检查我一般写成固定脚本每次换数据源都跑一遍比肉眼翻属性表可靠。还有一个验证习惯拿几个有公开登陆记录的台风年份做对照不比对具体路径只比对“是否有登陆、大致月份、影响区域”三件事。如果某年数据里完全找不到某次明显登陆过程的点先怀疑时间字段解析错了而不是怀疑数据缺失。时间字段补零不完整时月份和日期会错位轨迹会整体偏移但图上看不出来只有和外部记录对照才会暴露。血泪经验是我最早做这类历史路径统计时直接拿GRADE字段做分级结果 1950 年代和 2000 年代的颜色标准不一致图例看起来正常统计表却对不上。从那以后每次拿到台风 shp我都强制走一遍“投影确认 → 字段补零 → 时间转整型 → 按编号去重 → 重算强度”这五步少一步后面就要返工。希望帮到你。本文还有配套的精品资源点击获取