简介:全球飞行航线矢量数据集以Esri Shapefile标准格式整理,面向地理信息系统(GIS)从业者、航空交通研究人员以及需要航线底图做可视化分析的学生群体,数据包共包含15个文件,压缩后大小约8MB。核心shp文件记录机场间的航线线段,dbf文件附带航班编号、航空公司、飞行频率等属性,prj文件明确采用WGS84坐标系统,shx、sbn、sbx等索引文件与qix快速索引配合,可提升在主流GIS软件中的加载和搜索效率。数据可直接用于航线密度分析以识别繁忙空中走廊,进行机场间飞行距离测算,结合起降时间数据划分航班高峰与低谷时段,也可叠加人口、经济等空间数据探索航空运输与区域发展的关系。压缩包内还提供元数据XML和跨平台支持文件,解压后即得完整数据集。已有743人学习下载,适合作为科研预研、课程实验或行业应用原型的基础数据资产。
1. 全球飞行航线数据shp格式:这份数据集到底装着什么
做航线可视化、物流中转分析或空域流量评估时,拿到一份“全球飞行航线数据shp格式”资源,很多人第一步就拖进GIS看线条,然后就没有然后了。实际上,一套完整的全球航线shp通常包含shp、shx、dbf、prj四个基础文件,有的还带cpg、sbn、sbx等附属件,里面既有机场到机场的OD直线,也有带高度、时间戳的真实航迹点串。两种几何结构对应完全不同的分析姿势,用错了轻则指标算错,重则整个图层渲染成乱麻。这篇笔记按“结构认知→正确加载→清洗转换→避坑→进阶”展开,适合用QGIS做航迹展示的从业者、用Python做路网分析的开发者,以及想把航线数据接进数据中台却被坐标系卡住的数据工程师。
2. 认识数据:投影、字段与三类几何
2.1 prj文件:先判断坐标是经纬度还是平面米
判断数据能不能直接用,第一件事不是打开地图,而是看prj文件。拿文本编辑器打开同名.prj,内容是一个WKT字符串。最常见的是WGS84经纬度,prj里写着GEOGCS["GCS_WGS_1984"],坐标范围在-180到180之间;另一种常见的坑是EPSG:3857 Web墨卡托,坐标范围是±20037508,很多人把这种数据直接用QGIS打开,发现航线图层完全对不上底图。
我一般拿到数据后会先跑一个快速检查,用Python打印数据范围,同时把prj内容读出来看一眼。这比在GIS里反复缩放效率高得多。下面这段是标准的WGS84地理坐标系prj内容,也是大多数全球航线shp采用的基准:
GEOGCS["GCS_WGS_1984", DATUM["D_WGS_1984", SPHEROID["WGS_1984",6378137.0,298.257223563]], PRIMEM["Greenwich",0.0], UNIT["Degree",0.0174532925199433]]注意一点:prj里的SPHEROID参数决定了椭球体,全球航线数据基本都锚定WGS84椭球,少数历史数据集会用WGS72或克拉索夫斯基椭球。如果prj里出现后者,做长距离测距时偏差可能到百米量级,对航路分析虽然不致命,但写进交付文档时容易被懂行的甲方挑出来。
2.2 属性表:哪些字段是拿来画线的,哪些是拿来分析的
把dbf表展开,一套正经的全球航线shp通常有这些字段:航班号或航段编号、起飞机场代码、降落机场代码、起降时间戳、巡航高度、地速,部分数据集还会带上所属航司代码、机型、是否国际航线等业务标记。字段类型基本是C(字符)、N(数值)、D(日期)三类。这里有一张我见过的典型字段结构,不同来源的数据差异不小,但核心字段基本是这几样:
| 字段名 | 类型 | 含义 | 分析用途 |
|---|---|---|---|
| FLT_ID | C | 航班号/航段编号 | 按航段筛选、去重 |
| DEP | C | 起飞机场代码 | OD聚合主键 |
| ARR | C | 降落机场代码 | OD聚合主键 |
| TIME_START | D | 起飞时间 | 时段流量分析 |
| FL_LEVEL | N | 巡航高度(英尺) | 空域分层统计 |
| SPEED | N | 地速(节) | 航速分布评估 |
| IS_INTL | C | Y/N是否国际航线 | 国内外分组 |
这些字段里,画线只需要几何部分,做统计则必须靠DEP和ARR两个属性聚合。很多人在这一步踩坑:直接用FLT_ID去重后发现航段数量比实际机场对数量多出好几倍,原因是一条航线一天飞很多班次,FLT_ID是每班的唯一标识,不是航线唯一标识。
2.3 几何形态:OD连线与真实航迹是两种物种
全球航线shp里最容易忽略的是几何形态混存。一套数据里可能同时存在两类要素:一类是机场到机场的OD连线,几何上是单部件直线,只有两个顶点;另一类是航班真实历史航迹,是多部件多段线,一条线可能包含几十甚至几百个顶点,每两个相邻顶点之间是一次航迹采样。这两类几何混在同一图层时,必须先区分再处理。区分方法很简单,读取shapeType和每个要素的points数量,OD直线polyline的point数等于2,真实航迹的point数远大于2。
处理方式完全不同:OD连线适合做网络拓扑分析和机场对频次统计,真实航迹适合做空域热力、高度剖面和航路偏移分析。把它俩混在一起画图,视觉上会看到一幅乱麻;混在一起统计,机场对流量指标会被同一趟航班的不同采样点反复放大。我习惯的做法是在读取阶段就按顶点数打标分组,而不是等分析时再拆。
3. 加载与快速质检:GIS软件和Python各一套
3.1 QGIS加载与要素观测
QGIS里加载shp基本是零门槛操作,直接拖拽文件进图层面板就行。但加载不等于能用,有两条必做的验证:第一,右键图层看属性表,核对字段数与类型是否符合预期,重点看dbf是否被完整读取;第二,用Identify工具随机点几个要素,确认几何坐标与底图对得上,如果航线整体落在海里或坐标系原点附近,基本是投影解读错了。
图层样式建议初始阶段用“分级”符号按FL_LEVEL着色,把透明度拉到60%以上。这能快速发现两类异常:一是高度字段里有大量0值或负值,说明原始数据做过裁剪或字段映射错位;二是几何自相交的航段在渲染时会明显纠缠在一起,这类要素后续分析时需要单独过滤。
3.2 用pyshp快速读字段结构与几何类型
在Python里做结构检查,我习惯用pyshp,因为它轻量、无依赖、读写速度快,就是专门吃shp的。下面这段代码可以在几秒内输出基础结构信息:
import shapefile sf = shapefile.Reader("global_routes.shp") print("要素总数:", len(sf)) print("几何类型:", sf.shapeType) # 3=PolyLine, 5=Polygon, 8=MultiPoint # 字段信息 for f in sf.fields[1:]: # 第一项是deletion flag,跳过 print("字段名:", f[0], "| 类型:", f[1], "| 长度:", f[2]) # 抽查前5个要素的顶点数 for i, shape in enumerate(sf.shapes()[:5]): print(f"要素{i}: 部件数={len(shape.parts)}, 顶点数={len(shape.points)}")逻辑说明:sf.fields的第一项是dbf内部的删除标记位,业务字段从索引1开始;shapeType的常量定义是pyshp库内置的,3代表PolyLine多段线,这是航线shp最常见的类型。参数说明:shape.parts表示多部件几何的顶点索引分段位置,如果parts长度大于1,说明这是多部件要素,后面清洗时要考虑“炸开”。
把这段代码跑完,如果发现shapeType是8或1而不是3,说明数据的几何类型和你预期不符——可能是点数据被误命名成航线,或者是面状的航班区域边界。这时候不要硬往下走,回去重新确认数据结构。
3.3 一套可固化的质检清单
加载环节最容易漏掉的是隐性损坏。shp格式是四件套,一个文件损坏整层数据都无法读取,但更隐蔽的问题是字段内容是乱码或数值越界。我做过一个小检查脚本,常驻在本地工具目录,每次拿新数据先跑一遍,输出一份“数据体检报告”。检查项包括:要素数量是否与dbf记录数一致、几何类型是否为多段线、是否存在空Geometry、坐标范围内是否有异常值(如经纬度大于180或小于-180)、字段缺失率超过10%的列是否要剔除。
还要检查要素之间的空间拓扑。航线shp通常允许航段互相交叉,这是正常现象,不需要做拓扑修正;但如果有要素的起点或终点不在机场点位附近,多半是坐标转换时字段错位,这种要素在聚合统计时会污染OD对结果。
4. 把航线数据变成能用的东西:炸开、坐标导出与格式转换
4.1 多部件要素炸开:让每条航段独立成行
真实航迹数据里,同一个航班可能被存储为一个多部件PolyLine,比如中途经停的航班,起飞段和降落段被存进同一个要素。做OD统计时,这种多部件要素会把两个航段算成一条,导致经停航线无法拆分。QGIS里用“多部件转单部件”工具可以一键完成,但我更推荐在Python里做,方便批量处理。下面是pyshp的实现方式:
import shapefile reader = shapefile.Reader("global_routes.shp") writer = shapefile.Writer("routes_single.shp", shapeType=3) # 复制原字段结构 for field in reader.fields[1:]: writer.field(*field) for record, shape in zip(reader.records(), reader.shapes()): if len(shape.parts) == 1: writer.record(*record) writer.shape(shape) else: # 多部件:按parts索引拆成多条单部件线 for idx, pt_idx in enumerate(shape.parts): end_idx = shape.parts[idx + 1] if idx + 1 < len(shape.parts) else len(shape.points) part_points = shape.points[pt_idx:end_idx] writer.record(*record) writer.shape(shape, 0) # 占位 writer.shapes[-1].parts = [0] writer.shapes[-1].points = part_points writer.close()逻辑说明:parts是int型数组,每个元素是一个顶点索引,表示该部件的起始位置。拆分的核心就是按这些索引把points数组切段,每段生成一个独立要素。参数说明:shape(shape, 0)里的第二个参数0表示加载原几何的部件0,写完后覆盖它的points和parts属性。需要提醒的是:拆分后的要素记录完全复制原记录的属性,如果后续需要标识“这是某航班的第几段”,应该在拆分时额外加一个shard_id字段。
4.2 坐标点串导出CSV:丢给Spark做航迹分析
航线shp在GIS里很好用,但进入大数据计算框架就别扭了。最常见的需求是把每条航段的顶点坐标展开成扁平表,一行为一个采样点。导出的CSV可以直接怼进Spark或Pandas做时间序列分析。锚点代码如下:
import csv import shapefile reader = shapefile.Reader("global_routes.shp") fields = [f[0] for f in reader.fields[1:]] with open("route_points.csv", "w", newline="") as fp: writer = csv.writer(fp) writer.writerow(fields + ["LON", "LAT", "ORDER"]) for record, shape in zip(reader.records(), reader.shapes()): rec = list(record) for i, (lon, lat) in enumerate(shape.points): writer.writerow(rec + [lon, lat, i])CSV导出参数变化的重点在ORDER列:它是顶点在原始航段内的顺序号,没有它,后续按航段分组还原航迹顺序时只能靠连线,判断不了是否倒序。另外,这个导出方式对多部件要素会把所有部件混在一个ORDER序列里,如果你在意部件边界,建议先在4.1步完成炸开再导出。
4.3 转GeoJSON:为什么我推荐geopandas
团队成员不都在用GIS软件时,GeoJSON是最好交接的格式,浏览器直接能渲染,后端也好解析。shp转GeoJSON最简单的方式是用geopandas,一条命令搞定:
import geopandas as gpd gdf = gpd.read_file("global_routes.shp", encoding="utf-8") gdf = gdf.to_crs("EPSG:4326") # 强制统一到WGS84经纬度 gdf.to_file("global_routes.geojson", driver="GeoJSON", encoding="utf-8")这里有个变量:read_file里的encoding参数不能省。很多shp的dbf是GBK或GB2312编码存储的,不指定encoding读出来就是乱码。再一个注意点是to_crs:如果源数据本来就是WGS84,这一步不会改变坐标;如果源数据是投影坐标系,这一步会做一次动态投影转换,把平面米换算成经纬度。GeoJSON规范本身不强制坐标顺序,但默认为[经度, 纬度]的顺序,geopandas导出时会自动处理好。
4.4 性能边界与大文件参数调整
全球航线数据往往在几十万到百万要素量级,这时候读取方式要对性能做取舍。上面pyshp是逐要素流式读取的,内存占用很低,适合超过内存的巨型shp;geopandas是一次性全部读入内存,百万要素的shp大概占2-4GB堆内存,机器小于16GB内存时很容易爆。
真遇到超大shp,建议分块读:geopandas没有内置分块,但可以用pyshp读几何、dbf用csv模块单独读,按要素批次合成GeoJSON。还有一个通用优化:shx索引文件决定了几何读取的跳过效率,分发数据集时shx不能丢,丢了shx,QGIS和pyshp都要全量扫描才能定位要素,百万级数据等待时间能从秒级拖到分钟级。
5. 常见问题与避坑:五个高频翻车点
5.1 打开shp只有点没有线
现象:在QGIS里加载后,图层显示为散落的点,看不到任何航线线条。
原因:部分全球航线shp为了减小体量,会把航段顶点独立存储为点要素,线要素的几何字段在导出时被清空,只保留属性。更常见的是shapeType本身为Point(类型1),不是PolyLine(类型3),文件没错,是数据组织方式不同。
解决:先确认shapeType,再决定是否要做“点连成线”。点转线在QGIS里用“Points to Path”工具,排序字段用ORDER,分组字段用FLT_ID。如果原本就是PolyLine但显示成点,检查QGIS的符号化设置,确认渲染层根本没有启用线样式。
5.2 dbf里的中文显示乱码
现象:属性表中机场名、航空公司名显示为“?????”或乱字符。
原因:dbf文件用GBK编码写入,但GIS软件默认按UTF-8解析。这个在国产数据源里经常出现,多个版本交替保存后cpg文件会丢失,导致编码探测失效。
解决:在QGIS数据源属性里手动设置编码为GBK或GB2312重新加载。用geopandas读取时加encoding="gbk"参数。彻底解决是统一转码保存一份UTF-8编码的副本,避免后续每次使用都要手动指定。
5.3 航线整体偏移到非洲西海岸
现象:图层能显示,但所有航线整体落在经度0度附近,明显偏离大陆轮廓。
原因:这是经典的坐标轴顺序反了。部分导出工具把坐标写成[纬度, 经度]顺序,而shp规范要求[经度, 纬度]顺序,读出来整体转置,视觉上就相当于经度做了平移。这类问题在看单个点时容易忽略,放在全球底图上立刻现形。
解决:用QGIS的“Fix geometries”工具无法处理,需要写脚本交换坐标。pyshp读取后把每个点的x和y互换再重写。这个坑不会报错、不会警告,最凶险,交付前务必叠加底图做一次目检。
5.4 字符串字段被截断
现象:读dbf时机场代码或航班号只能读到前几个字符,后半截丢失。
原因:shp的dbf字段长度是建表时固定死的,比如DEP字段声明为C 4,而后面的写入程序往里面塞了6个字符,读取端按固定长度切片,超过部分被吞掉。这是dbf格式的老毛病,宽字段被窄字段覆盖写入时最容易触发。
解决:用pyshp读取时手动指定字段长度大于声明长度,或者用先探测再读取的方式,扫描整列判断最长值。最稳妥的方案是放弃dbf,把属性另存为CSV或Parquet做外挂关联,用ID字段和shp几何配对,彻底绕开dbf长度限制。
5.5 统计OD对数量时翻倍出错
现象:按起降机场聚合航线数量,发现结果大致是实际航线的两倍。
原因:OD统计通常用“机场A到机场B”和“机场B到机场A”两个方向分别计数,如果数据源把往返航班放在同一字段组而不区分方向,聚合时就会被拆成两条记录。另一个常见场景是经停航班的多部件没炸开,多段被当成了单段。
解决:聚合前先明确方向口径。做无向OD统计时,把(origin, dest)排序后做标准化,让“A→B”和“B→A”归并到同一个key;做有向统计时保留顺序。加上这一步之后,OD对数量才会和机场对实际联通的期望值吻合。
6. 进阶技巧:按机场对做航段密度过滤,直接定位高频航线
航线数据做聚合时,很多人只会画出全部航段,结果图上一片密密麻麻的线,什么信号都看不清。一个很实用的处理是按起降机场对聚合,过滤掉只飞过一两次的冷门航段,只保留高频核心航线。这种方法能快速看出整个航线网络的主干结构,也适合用来验证数据覆盖是否完整。
先写一个按DEP-ARR聚合的脚本:
from collections import Counter pairs = Counter((r["DEP"], r["ARR"]) for r in reader.records()) frequent = {k for k, v in pairs.items() if v >= 5} # 过滤低频筛选航段的逻辑很简单:对每条记录的DEP和ARR组合计数,阈值设为5,也就是一个机场对至少要有5条航段记录才保留。这个阈值不是死的,数据量大的时候我会调到10或者20,看线条密度和业务关注粒度来定。要注意的是OD直线数据里每趟航班一条线,直接按频次过滤就好;真实航迹数据建议先按航段合并再聚合,否则同一个航班的不同采样点会反复计入计数。
过滤完之后还有一个值得做的验证:把过滤后的航段导出为GeoJSON,拖进地图上对照真实的航线网络形态。正常情况下高频航线会自然形成若干条明显的“走廊”结构,比如跨洋主干线、区域性辐射线。如果过滤后被保留下来的航线非常稀疏且毫无规律,大概率是数据里根本没有足够重复样本,或者原始数据做过抽样,这种情况就不要用频次过滤,放宽阈值或者直接用全量数据渲染。
最后还有一个针对点串航迹的操作习惯:把高频航段的顶点合并成一个热力点集,在QGIS里用Heatmap渲染,整个空域流量分布一眼就能看清。这个做法的参数是热力半径,我一般先设0.5度试跑,看渲染效果再逐步缩小到0.1度。一次在做某货运模拟项目X时,我把一套全球航线按这个流程处理后,几条高密度走廊的走向和预期的货运枢纽分布完全对得上,从那以后我每次拿到航线shp都强制先跑一遍结构检查脚本再进入分析,这套流程帮我挡掉了很多次图层错位和字段截断的坑。希望帮到你。
本文还有配套的精品资源,点击获取