简介:全球飞行航线数据以shp格式封装,面向GIS研究者、航空交通分析人员及空间数据分析学习者,可用于航线密度评估、最短路径测算与区域航空关联研究。打包共15个文件,压缩后大小8.01MB,核心包括存储航线几何的shp文件、记录航班属性(如航班号、航空公司)的dbf文件、定义坐标投影的prj文件,以及shx、sbn/sbx、qix等索引文件,便于在ArcGIS、QGIS中快速加载与查询。数据采用WGS84全球坐标系统,适合进行跨区域的航线可视化与空间统计分析,同时附带的xml元数据补充了数据说明,便于使用者确认投影信息和数据来源。该数据包支持的典型分析场景包括航线密度分析、机场距离计算、起降时段分布以及结合人口经济数据的关联挖掘,可直接用于教学演示、科研预研和行业初步评估。目前已有743人浏览学习,适合初步掌握GIS操作、希望开展全球航线流量研究或结合其他地理数据做关联分析的用户直接使用。
1. 全球飞行航线数据shp格式:一张能直接落地的全球航路底图
把全球飞行航线数据shp格式打开后,第一个反应通常是:图呢?明明有几十万个要素,画出来却是一团乱麻。这份数据的价值恰恰藏在这团乱麻里——每条线代表一条可公开获取的航线,属性表里带着起降点、航司和班次信息,能直接用来做航线覆盖分析、物流可达性评估和可视化大屏底图。真正需要花时间处理的不是“打开数据”,而是看清它用的是哪种几何形态、字段里有没有缺值,再用一套标准流程把数据洗干净。接下来就按我日常处理的空间数据顺序,把这条完整链路走一遍:文件结构、坐标基准、Python清洗、QGIS出图,最后是踩坑排查。新手可以照着步骤走,熟手可以直接跳到参数细节和常见的翻车点。
2. 拆开全球航线shp文件:文件构成、几何形态与坐标基准
2.1 一个shp图层由shp、shx、dbf三个文件组成
拿到一个名为“global_routes.shp”的数据包时,同目录下通常还会有一串同名文件。这些不是冗余,而是一个完整矢量图层拆成了不同职责的文件。.shp保存几何坐标,也就是每条航线的折点位置;.shx是空间索引,帮助GIS软件快速定位某条要素;.dbf保存属性表,每一行对应一条航线,字段里是航司代码、起飞机场、到达机场等信息。三者缺一不可,只有.shp文件时很多软件会直接报错。
| 文件 | 作用 | 缺失后果 |
|---|---|---|
| .shp | 几何坐标存储 | 图层无法打开 |
| .shx | 空间索引 | 读取变慢,某些工具直接失败 |
| .dbf | 属性字段存储 | 只能看到线,看不到任何属性 |
| .prj | 坐标系描述 | 坐标系被误判,位置偏移几百公里 |
其中.prj文件很容易被忽略,但它决定了数据的“地理身份”。全球航线数据几乎都是WGS84经纬度坐标,也就是EPSG:4326,.prj里写的就是这一段描述。缺少.prj时,QGIS会按“未定义坐标系”加载,ArcGIS会套用一种默认坐标系去解析,结果数据还是那些坐标,画出来却偏到完全不对的位置。
属性表字段命名也有历史包袱:dbf格式规定字段名最长10字节,所以你会看到airline_iata、from_icao、to_icao这类缩写,而不是destination_airport_icao这种长名字。这个限制在后续用GeoPandas写文件时还会再踩一次,后面会专门讲。
2.2 两种几何形态:整条航线折线与逐航段短线
全球航线shp数据的几何形态主要有两种,搞混了会直接影响统计口径。
第一种是“整条航线作为一个LineString”。一条记录的几何覆盖从起点到终点的完整路径,折点可能是2个,也就是从A点直接拉一条直线;也可能有几十个折点,把真实飞行路径上的转向点都串进去了。这种形态适合画网络图,一条线代表一条航线,视觉干净。
第二种是“逐航段拆分”。数据把一次完整飞行按飞行阶段拆成多条短线段,比如从某内陆机场到某沿海门户算一段,从门户到跨洋目的地再算一段。每条短线段单独一行记录,字段里通常有一个segment_no或leg_id来标识航段顺序。
判定方法很简单:用Python读进来之后查看geom_type,并统计每条线的折点数。如果全部是LineString且大量记录只有2个点,就是第一种;如果同一条航线有多行、每行都是一段短折线,就是第二种。统计口径要跟着形态走:按航司统计航线条数时,第二种必须先按起降点对去重;按机场计算进出港流量时,两种都要按字段聚合,而不是直接数行数。
2.3 全球数据默认WGS84经纬度,为什么不能直接算距离
全球航线shp默认使用WGS84经纬度坐标,单位是度,不是米。这是地理坐标系,不是投影坐标系。两个坐标点之间的“几何长度”算出来是度,而同样的1度经度在赤道附近约111公里,在高纬度地区可能只有几十公里。如果拿着这个长度当真实飞行距离用,所有高纬度航线都会被严重低估。
所以处理流程要拆成两段:做可视化和网络关系分析时,保持EPSG:4326不变就好,因为底图服务和大多数在线地图都认经纬度;做距离、速度、油耗这类量化指标时,必须先把数据投影到以米为单位的坐标系,算完再转回来。我常用的投影是等距圆柱投影,参数为+proj=eqc +R=6371000 +units=m,把地球近似成半径6371公里的球体,全球范围都能用,但也不是高精度方案。如果只研究某个特定区域,用该区域的UTM投影更稳妥。
3. 用GeoPandas读取并清洗全球航线shp:最小复现流程
3.1 读取shp,先核对字段、坐标与几何类型
清洗的第一步不急着过滤,先做体检。用GeoPandas读文件,核对字段、坐标系、几何类型和要素总量。
import geopandas as gpd # 读取shp,read_file只需要给.shp路径,同名.shx/.dbf会自动关联 routes = gpd.read_file("data/global_routes.shp", encoding="utf-8") # 查看前5行属性,确认字段名和数据样例 print(routes.head(5).to_string()) # 查看坐标系,None时结合坐标范围判断真实情况 print("坐标系:", routes.crs) # 几何类型分布,确认是LineString而不是Point print(routes.geom_type.value_counts()) # 要素总量和字段列表 print("要素数量:", len(routes)) print(routes.columns.tolist())逻辑说明:read_file只需要传入.shp路径,同目录下的.shx和.dbf会自动关联加载。encoding参数控制属性表解码方式,utf-8兼容英文,但遇到中文乱码时要换成gbk再试。geom_type.value_counts()能一眼看出几何类型,正常结果应该只有LineString或MultiLineString。如果出现Point,说明这份shp的几何是机场点而不是航线,需要在后面的流程里重建几何。
3.2 按字段筛选有效航线和剔除空几何
体检完开始清洗。全球航线数据里最常见的脏数据是起降机场代码为空、几何为空、几何非法,以及混入了非线要素。这些不处理,后面按航司筛选、按区域统计都会出错。
# 检查关键字段缺失情况 print(routes[["airline", "from_icao", "to_icao"]].isna().sum()) # 剔除起降代码为空的行 routes_ok = routes.dropna(subset=["from_icao", "to_icao"]) # 剔除空几何和非法几何 routes_ok = routes_ok[routes_ok.geometry.notna()] routes_ok = routes_ok[routes_ok.geometry.is_valid] # 只保留线要素,剔除个别点或面混入的情况 routes_ok = routes_ok[routes_ok.geometry.geom_type.isin([ "LineString", "MultiLineString" ])] print(f"清洗前 {len(routes)} 条 -> 清洗后 {len(routes_ok)} 条")参数说明:dropna(subset=["from_icao", "to_icao"])只删关键字段为空的,航班频次之类字段缺失暂时不影响结构。is_valid是GeoPandas内置拓扑检查,自相交、闭环断裂这类问题都会被标记为False。如果只有少数几何非法,直接删掉最省事;如果比例超过5%,建议检查原始数据处理环节,而不是在这里删数据。
3.3 计算航段公里数、几何简化与导出shp
数据干净了,还不能马上出图。全球航线shp的折点数量通常很大,直接渲染会卡;另外需要把几何长度转成公里数,用来筛掉异常短线和做后续分析。
from pyproj import CRS, Transformer # 航线shp是经纬度,先用等距圆柱投影转成米制再算长度 # 参数含义:R=6371000表示把地球近似为半径6371公里的球体 projector = Transformer.from_crs( CRS.from_epsg(4326), CRS.from_proj("+proj=eqc +R=6371000 +lat_ts=0 +lon_0=0 +units=m"), always_xy=True, ).transform # 利用投影后的几何计算每个航段的长度 routes_proj = routes_ok.copy() routes_proj["geometry"] = routes_proj.geometry.to_crs( "+proj=eqc +R=6371000 +lat_ts=0 +lon_0=0 +units=m" ) routes_ok["length_km"] = (routes_proj.geometry.length / 1000.0).round(1) # 剔除50公里以下的异常短航段,通常是同一城市双机场摆渡或拓扑错误 routes_clean = routes_ok[routes_ok["length_km"] >= 50] # 简化几何,0.01度约等于1公里,保留航线走向的同时降低折点密度 routes_clean["geometry"] = routes_clean.geometry.simplify( tolerance=0.01, preserve_topology=True ) # 导出清洗后的shp routes_clean.to_file("data/routes_clean.shp", encoding="utf-8")参数说明:to_crs里的投影字符串用了等距圆柱投影,把全球坐标统一投影到米制,算出来的length_km是球面近似距离,不是高精度大圆距离。如果做飞行时间估算,建议改用大圆算法,后面会提到。tolerance=0.01的单位跟随当前CRS,因为原数据是4326,这里的0.01就是0.01度,约1.1公里,对全球航线图来说既能保留主要走向,又能显著减少文件体积。preserve_topology=True保证简化后线不会自相交。
这里有个实际选择:如果只是为了出图,几何简化可以放到最后;如果还要做空间连接和网格聚合,一定要先简化再做,否则连接计算会慢好几倍。
4. 在QGIS中把全球航线shp做成一张可读航线图:加载到出图全流程
4.1 加载后的第一件事:看属性表、设基础样式
把清洗后的shp拖进QGIS,第一件事不是配颜色,而是打开属性表,把from_icao和to_icao排序,看有没有大量空值。空值会让分类渲染出现一个巨大的“空”类目,干扰判断。
在“图层属性 → 通用 → 查询过滤器”里写表达式,把没有端点机场的线排除掉。QGIS表达式写法如下:
"from_icao" <> '' AND "to_icao" <> ''然后回到“符号化”标签,选择“简单线”,线宽改成0.1,颜色用半透明的灰色。全球航线数据密度非常高,默认的0.26毫米蓝色实线会糊成一块墨团,看不了任何细节。细线+半透明是第一层通用配置,先把整体骨架看清,再谈专题渲染。
4.2 按航司或区域分色,让航线网络可读
做主题分析时,推荐用“规则式渲染”(Rule-based Rendering)而不是纯分类渲染。分类渲染按字段的每个取值逐一生成一类,航司字段如果有几十个值,图例长到没法看。规则式渲染可以自己写表达式,只挑重点航司着色,其余统一置灰。
"airline_iata" = '某航司代码'每条规则给一个颜色,线宽可以加大到0.3,其余不匹配的规则最后加一个ELSE,用灰色细线兜底。如果按区域看结构,可以用坐标表达式限定范围,例如:
x_at( $geometry ) > 100 AND x_at( $geometry ) < 180这表示只显示经度100度以东的航线。注意x_at($geometry)取的是线的第一个折点坐标,不代表整条线都在这个区域内。要精确限定区域,正确做法是“矢量 → 地理处理工具 → 裁剪”,先按面图层裁剪,再对裁剪结果做渲染。
4.3 大图层不卡顿的3个关键选项
全球航线shp动辄几十万要素,QGIS默认状态下每次缩放旋转都会重新渲染全部线,卡顿几乎是必然的。三个设置能明显改善,且不改变源数据:
第一,在“符号化 → 线 → 绘制效果”里勾选“简化”,QGIS会在缩小视图时自动降低折点密度,细节少渲染。第二,在“图层属性 → 渲染”里勾选“仅在指定缩放级别内可见”,比如设置最大缩放级别1:2000000,拉近之后就不再绘制这层密集航线。第三,如果还要继续叠加其他要素,把航线图层设置为“充当缓存图层的基础”,避免每次刷新都重算一次。
这三个都是渲染期优化,不是数据简化,所以不影响任何分析结果。如果做完这三步还是卡,再回头检查数据本身是不是折点太多,用上一章的simplify再做一轮。
5. 全球航线shp数据的踩坑指南:5个高频问题与排查办法
5.1 读出来的全是点而不是线
现象:打开shp后,图层是密密麻麻的点,没有任何连线。
原因:很多名为“flight_routes.shp”的数据集其实只包含机场点坐标,航线关系存放在一个单独的CSV表里。数据包里没有把CSV和点坐标合成线,所以“航线数据”里压根没有线几何。
解决:手动用起降点代码把两端坐标拼接成LineString。用GeoPandas做一次连接:
import geopandas as gpd from shapely.geometry import LineString # airports是机场点数据,routes是带from_icao/to_icao的航线表 airports = gpd.read_file("data/airports.shp") routes = gpd.read_file("data/routes_table.csv") # 把起飞机场坐标连接进来 merged = routes.merge( airports[["icao", "geometry"]].rename(columns={"geometry": "from_geom"}), left_on="from_icao", right_on="icao", ) # 把到达机场坐标再连接进来 merged = merged.merge( airports[["icao", "geometry"]].rename(columns={"geometry": "to_geom"}), left_on="to_icao", right_on="icao", ) def build_line(row): from_pt = row["from_geom"].coords[0] to_pt = row["to_geom"].coords[0] return LineString([from_pt, to_pt]) merged["geometry"] = merged.apply(build_line, axis=1) result = gpd.GeoDataFrame( merged[["airline", "from_icao", "to_icao", "geometry"]], crs="EPSG:4326", ) result.to_file("data/routes_built.shp", encoding="utf-8")逻辑说明:两次merge分别把起点和终点的坐标取出来,build_line函数读取首尾坐标构造线。注意coords[0]只取第一个点,如果机场要素是MultiPoint,要先做representative_point()。另外这样生成的是直线段,不是大圆航线,跨洲长航线在球面上会差不少,做精确展示时要在线上补充插值点。
5.2 坐标范围正常,位置却偏移
现象:数据源明明标注WGS84,导入后航线整体偏移,有些图层还提示未知坐标系。
原因:缺少.prj文件时,软件无法识别坐标系。ArcGIS会按默认坐标系解析,QGIS则会挂起待处理状态。坐标本身没变,是解释坐标的基准错了。
解决:先看坐标范围。经纬度数据的x范围在-180到180之间,y在-90到90之间。如果你的x是百万数量级,那是投影坐标,要重新定义而不是硬转。用set_crs给数据补上EPSG:4326:
from shapely.geometry import shape import geopandas as gpd # 读进来坐标范围正常但Crs为None时,直接定义坐标系 routes = gpd.read_file("data/routes_wrong.shp") routes = routes.set_crs("EPSG:4326") # 如果确实需要投影到平面坐标系,再单独转换 routes_projected = routes.to_crs("EPSG:3857")注意顺序:先定义原始坐标,再投影。反过来的话,基于错误坐标系的投影会被连续歪曲,且很难复原。遇到这种状况,先检查源文件的.prj是否丢失,再决定是定义坐标系还是重投影,这条规矩能少走很多弯路。
5.3 航线太多,打开和渲染都很卡
现象:在QGIS里缩放视图时一直转圈,任何操作都要等好几秒。
原因:几十万条线,每条线有几十甚至几百个折点,压缩和解压损失巨大。尤其是从GPS轨迹打包成的航线数据,折点数量极多,数据显示压力远远超过要素数量本身。
解决:先用simplify把折点砍掉一部分,再进QGIS。调参原则是:全球尺度下0.01度够用,跨大洲长航线可以放宽到0.05度;如果做城市周边短航线,容差要降到0.001度,否则短航线会变形。渲染上配合第4章的三个设置,卡顿能明显缓解。
5.4 属性字段缺失,按航司筛选一片灰
现象:在分类渲染里选airline字段,图例里只有一个空类,所有线都是灰色。
原因:航线字段大量为空字符串或空格,或者同一字段里混用了IATA代码和ICAO代码两种标准,造成分类器识别不出有效取值。
解决:先统计缺失量,再用清洗逻辑补全:
print(routes["airline"].isna().sum()) print(routes["airline"].value_counts().head(20)) # 统一把空串和空值替换为unknown routes["airline"] = routes["airline"].fillna("unknown") routes["airline"] = routes["airline"].replace("", "unknown")补全后重新查看value_counts(),如果还有大量unknown,说明数据源本身字段就是残缺的,考虑从关联表补录航司代码。
5.5 导入ArcGIS提示字段名被截断
现象:在GeoPandas里建了一个字段叫destination_airport_icao,导出shp再用ArcGIS打开,字段名只剩destinatio。
原因:dbf格式规定字段名最长10字节,GeoPandas导出时不会报错,也不会主动截断,但ArcGIS读取时按规范截断,导致字段名对不上。
解决:导出前把长字段名改成短ASCII名,用简洁的缩写。导出后立刻回读一次检查字段是否完整,确认匹配无误后再使用。
# 导出前字段改名 routes_clean = routes_clean.rename(columns={ "destination_airport_icao": "dest_icao", "airline_operator_name": "airline", }) routes_clean.to_file("data/routes_shortfield.shp", encoding="utf-8")这部分是纯经验,不知道这个限制的时候会莫名其妙丢字段,知道之后每次导出前都会习惯性检查一遍字段名长度,算是换来的习惯。
6. 用航线shp做一张全球热力航线图:网格聚合与出图技巧
最后分享一个我常用的出图技巧:把密集的航线转成网格热力面,观感比几千根线叠在一起好很多。
在QGIS里,用“矢量 → 研究工具 → 创建渔网”生成一个全球2度间隔的网格,然后把航线图层与网格做“空间连接”,统计每个网格内经过的航线数量。有两点需要注意:网格范围要覆盖全球,如果只覆盖部分区域,结果图会缺角;空间连接的策略选择“相交”,因为一条航线可能穿过多个网格,每条相交记录都会被统计进去。
出图参数上,深色底图配亮色航线永远是最高效的组合。把网格图层的渲染方式改为“分级符号”,字段选连接统计出的航线数,色带从深蓝到亮黄,再把这个网格放在航线图层下方。图例标题不要保留“join_count”这种字段名,改成“航线经过数量/网格”,看图的人一眼就懂。
用脚本自动化网格聚合时,可以在GeoPandas里直接做,关键步骤是先建网格再空间连接:
import geopandas as gpd import numpy as np from shapely.geometry import box routes = gpd.read_file("data/routes_clean.shp") # 生成全球2度x2度网格,经纬度范围固定 xs = np.arange(-180, 180, 2) ys = np.arange(-90, 90, 2) grid = gpd.GeoDataFrame( geometry=[box(x, y, x + 2, y + 2) for y in ys for x in xs], crs="EPSG:4326", ) # 空间连接统计每个网格命中的航线数量 joined = gpd.sjoin(routes, grid, how="inner", predicate="intersects") count = joined.groupby("index_right").size().reset_index(name="route_count") grid = grid.reset_index() grid = grid.merge(count, left_on="index", right_on="index_right", how="left") grid["route_count"] = grid["route_count"].fillna(0).astype(int) grid.to_file("data/grid_routes_count.shp", encoding="utf-8")这段代码里,sjoin的predicate="intersects"判断线与网格是否相交,groupby("index_right")按被命中的网格索引分组计数。网格大小要根据展示目的调整:2度格适合全球层面,0.5度格适合观察某区域内部的短途航线,网格越大,热力越平滑,但局部差异也越模糊。
关于这套方案的最后一层经验:航线热力图不是越密越好。第一次做的时候我把网格设成0.5度,全球图变成密密麻麻的雪花点,完全看不出东西;改成5度之后又抹平了东西部差异,故事讲不出来。最后发现,网格粒度跟着叙事走,讲全球连通性用2到5度,讲局部航司竞争用0.5度,这才是这份数据最有用的打开方式。每次拿到新的全球航线shp,我都习惯先问三句:几何是不是线?坐标是不是度?字段缺不缺?这三个问题答完,剩下的事就是调样式了。这些习惯都是从一次次翻车里换回来的,希望帮到你。
本文还有配套的精品资源,点击获取