简介:这份资源面向GIS从业者、城市规划与交通研究人员及地理信息相关专业师生,提供2022年7月整理的四川省地理空间数据,可用于行政区域分析、交通网络规划、空间查询与专题制图等场景。压缩包共33个文件,约66.64MB,以shp、dbf、prj、shx等Shapefile核心格式为主,辅以sbn、sbx空间索引与xml元数据文件,覆盖省级、地市级、区县级行政边界,以及道路网与铁路网数据,属性表与投影信息齐全,可直接在ArcGIS、QGIS等软件中加载使用。借助这些数据,读者能够比较不同层级行政区域、评估路网布局与交通瓶颈,或将交通与行政图层叠加制作直观地图,并结合人口、经济等外部数据构建分析模型。目前已有2630人浏览学习,适合需要四川省基础地理底图与交通网络数据的中高级GIS用户参考使用。
1. 拿到一份 2022.07 的四川 SHP 包,先别急着拖进 ArcGIS
上周有个做物流选址的读者发来一份压缩包,文件名是「【SHP文件-2022.07】四川省行政区划(省、市、县)+道路网+公路网」,问我能不能直接拿来做配送分区。我打开一看,省、市、县三级行政边界齐全,道路网和公路网分层存放,坐标系是常见的 CGCS2000 地理坐标。这类资源在 GIS 圈子里属于「基础底图级」素材——做四川范围内的空间分析、路网可达性、行政区划统计,基本都绕不开它。
但问题也恰恰出在「基础」两个字上。很多人拿到 SHP 包的第一反应是双击打开、拖进软件、开始画图,结果要么是中文属性乱码,要么是道路网和行政区划对不上位置,要么是面积算出来差了几个数量级。这份 2022.07 的数据本身质量不差,坑主要出在使用方式上。下面我按「先搞清楚里面有什么、再动手做一次完整加载、最后把常见翻车点过一遍」的顺序拆开讲,新手能照着复现,熟手可以重点看坐标系和拓扑那两节。
2. 拆包先看结构:省、市、县三级边界与路网分层怎么读
2.1 文件清单与图层对应关系
拿到压缩包后不要直接解压到中文路径下,这是第一个血泪经验。SHP 格式对路径中的中文和空格容忍度很低,尤其是后续用 Python 的 geopandas 或 GDAL 读取时,路径里带中文经常报「文件不存在」或「无法识别数据源」。我一般会先解压到一个纯英文短路径,比如D:/gis_sc/,然后再看目录结构。
一份典型的四川行政区划加路网 SHP 包,解压后通常包含以下几组文件。每组 SHP 至少由.shp、.shx、.dbf、.prj四个文件组成,缺一不可。很多人只拷贝.shp就发给别人,结果对方打开后属性表全空,就是因为.dbf没带上。
| 图层组 | 常见文件名示例 | 几何类型 | 关键属性字段 |
|---|---|---|---|
| 省级边界 | sc_province.shp | Polygon | 省名、省代码 |
| 市级边界 | sc_city.shp | Polygon | 市名、市代码、所属省 |
| 县级边界 | sc_county.shp | Polygon | 县名、县代码、所属市 |
| 道路网 | sc_road.shp | LineString | 道路名称、等级、长度 |
| 公路网 | sc_highway.shp | LineString | 公路编号、等级、类型 |
需要留意的是,不同来源的 SHP 包在字段命名上差异很大。有的用拼音缩写,有的用中文全称,有的干脆只有NAME和CODE两个字段。2022.07 这个时间点意味着行政区划数据大概率已经包含了当年最新的乡镇级调整结果,但县级以上的边界变动不大,拿来和旧版对比时主要看属性表里的代码是否一致。
2.2 坐标系与投影:为什么你的面积算出来是错的
这是最容易被忽略、后果最严重的一节。SHP 包里的.prj文件决定了数据的坐标系。四川常见的坐标系有两种:一种是 CGCS2000 地理坐标系(经纬度,单位是度),另一种是 CGCS2000 高斯-克吕格投影(单位是米)。如果你拿到的是地理坐标系,直接算面积会得到一个以「平方度」为单位的数字,完全没有物理意义。
判断方法很简单:用记事本打开.prj文件,看里面有没有Gauss_Kruger或CGCS2000_3_Degree_GK_Zone之类的字样。如果有,说明已经是投影坐标,单位是米,可以直接算面积。如果只有GCS_China_Geodetic_Coordinate_System_2000,那就是地理坐标,必须先投影。
我一般会统一转到 CGCS2000 3 度带高斯投影,中央经线根据四川的经度范围选 102°E 或 105°E。下面这段 Python 代码演示了如何用 geopandas 读取并检查坐标系,然后做一次投影转换。
import geopandas as gpd # 读取县级边界,路径用纯英文 county = gpd.read_file("D:/gis_sc/sc_county.shp") # 查看当前坐标系 print("原始 CRS:", county.crs) # 如果单位是度,转为 CGCS2000 3度带投影(以105E为例) if county.crs and county.crs.unit_name == "degree": county_proj = county.to_crs("EPSG:4544") # CGCS2000 / 3-degree Gauss-Kruger CM 105E print("投影后 CRS:", county_proj.crs) else: county_proj = county # 计算每个县的面积(平方公里) county_proj["area_km2"] = county_proj.geometry.area / 1e6 print(county_proj[["NAME", "area_km2"]].head())这段代码的逻辑是:先读入数据,检查crs.unit_name是否为degree,如果是就调用to_crs转到 EPSG:4544。EPSG:4544 对应的是 CGCS2000 3 度带中央经线 105°E,覆盖四川大部分地区。如果你的数据偏西,比如甘孜、阿坝一带,可以换成 EPSG:4542(中央经线 102°E)。转换后再算面积,单位就是平方米,除以 1e6 得到平方公里。
注意:投影转换不是「越准越好」,而是「越合适越好」。四川东西跨度大,用一个中央经线覆盖全省会有边缘变形,做全省统计时建议按经度分带处理,或者直接用 Albers 等积投影。
3. 把 SHP 加载进 QGIS 和 Python:两条可复现的路径
3.1 QGIS 手动加载与属性表检查
如果你只是想做可视化或者快速检查数据,QGIS 是最省事的入口。打开 QGIS 后,直接把解压目录下的.shp文件拖进图层面板即可。但有几个细节决定了你看到的是不是「正确」的数据。
第一,编码问题。SHP 的属性表默认编码可能是 GBK 或 UTF-8,QGIS 有时会自动识别错,导致中文县名显示为乱码。解决办法是右键图层 → 属性 → 源,在「数据源编码」里手动选 GBK 或 UTF-8,切换一次就能看到正常中文。
第二,图层顺序。道路网和公路网是线图层,行政区划是面图层。如果线图层在面图层下面,会被面图层遮住。正确的顺序是:面图层在下,线图层在上,并且给面图层设置一定的透明度(比如 30%),这样既能看清边界,又能看到路网走向。
第三,属性表检查。右键图层 → 打开属性表,重点看三列:名称字段、代码字段、几何字段。名称字段确认没有乱码,代码字段确认位数一致(比如县级代码通常是 6 位),几何字段确认没有空值或无效几何。如果发现某个县的几何为空,说明数据在制作时就有缺失,需要单独补。
3.2 Python 批量读取与路网长度统计
如果你要做批量分析,比如统计每个市的公路总里程,Python 比 QGIS 更高效。下面这段代码演示了如何一次性读取行政区划和公路网,做空间连接后按市汇总里程。
import geopandas as gpd import pandas as pd # 读取市级边界和公路网 city = gpd.read_file("D:/gis_sc/sc_city.shp") highway = gpd.read_file("D:/gis_sc/sc_highway.shp") # 统一投影到 EPSG:4544,保证长度单位是米 city = city.to_crs("EPSG:4544") highway = highway.to_crs("EPSG:4544") # 给公路网打上所属市的标签(空间连接) highway_with_city = gpd.sjoin(highway, city, how="inner", predicate="intersects") # 按市名汇总公路长度 highway_with_city["length_km"] = highway_with_city.geometry.length / 1000 summary = highway_with_city.groupby("NAME")["length_km"].sum().reset_index() summary = summary.sort_values("length_km", ascending=False) print(summary.head(10))这段代码的关键在gpd.sjoin这一步。它把每条公路和它相交的市关联起来,predicate="intersects"表示只要几何有交集就算。如果一条公路跨了两个市,它会被拆成两条记录分别归入两个市,这是符合统计逻辑的。length_km用投影后的几何长度除以 1000 得到公里数。最后按市名分组求和,就能得到每个市的公路总里程排名。
提示:
sjoin之前一定要确认两个图层的 CRS 一致,否则空间关系判断会出错。我一般会在读取后立刻统一to_crs,不依赖原始文件的坐标系。
4. 避坑与排查:SHP 使用中最容易翻车的五个点
4.1 中文乱码:现象是县名显示为问号或方块
现象:打开属性表,中文名称字段显示为????或乱码方块。原因:SHP 的.dbf文件编码与软件默认解码方式不匹配,常见于 GBK 编码的数据在 UTF-8 环境下打开。解决:在 QGIS 中手动切换数据源编码;在 Python 中用gpd.read_file(..., encoding="gbk")指定编码。如果还是不行,可以用dbfread库单独读.dbf确认原始编码。
4.2 几何无效:现象是面积计算出错或空间分析报错
现象:做buffer或intersection时提示「无效几何」。原因:部分多边形存在自相交、重复节点或环方向错误。解决:用geopandas的buffer(0)做一次修复,或者用make_valid方法。修复后再做空间分析,结果会稳定很多。
from shapely.validation import make_valid county["geometry"] = county.geometry.apply(make_valid)4.3 路网与行政区划对不上:现象是公路跑到省界外面
现象:把公路网和行政区划叠加后,发现部分公路线段明显超出了省界范围。原因:路网数据的覆盖范围可能比行政区划大,或者两者坐标系不一致。解决:先统一 CRS,再用clip按省界裁剪路网。如果裁剪后仍有少量溢出,检查是否是省界本身有简化导致的边界误差。
4.4 字段类型错误:现象是代码字段被识别为数字,前导零丢失
现象:县级代码010001变成10001。原因:.dbf中代码字段被存为数值型,读取时自动去掉了前导零。解决:在读取时用dtype={"CODE": str}强制指定为字符串,或者在 QGIS 中新建一个文本字段重新赋值。
4.5 文件缺失:现象是打开时提示「缺少 .shx 文件」
现象:只拷贝了.shp和.dbf,打开时报错。原因:SHP 是复合格式,.shx是索引文件,.prj是投影文件,缺任何一个都可能导致读取失败或信息丢失。解决:分发和拷贝时务必把同名的一整套文件一起带上,建议直接打包整个文件夹。
5. 进阶用法:用这份数据做一次路网可达性快速评估
前面讲的都是「怎么把数据用起来」,这一章说一个具体技巧:用这份 SHP 包做一次简易的路网可达性评估。不需要复杂的网络分析插件,用 Python 的networkx加geopandas就能跑出一个粗略结果。
思路是这样的:把公路网转成图结构,节点是道路交点,边是路段,边的权重是长度。然后选一个中心城市(比如成都),计算到其他各县的最短路径距离。这个结果虽然不如专业路网分析精确,但用来做初步筛选足够了。
import geopandas as gpd import networkx as nx from shapely.geometry import Point # 读取公路网和县级边界 highway = gpd.read_file("D:/gis_sc/sc_highway.shp").to_crs("EPSG:4544") county = gpd.read_file("D:/gis_sc/sc_county.shp").to_crs("EPSG:4544") # 构建图:用路段端点作为节点 G = nx.Graph() for _, row in highway.iterrows(): line = row.geometry if line is None or line.is_empty: continue coords = list(line.coords) start, end = coords[0], coords[-1] length = line.length / 1000 # 公里 G.add_edge(start, end, weight=length) # 选一个起点(成都市中心附近的节点) start_node = min(G.nodes, key=lambda n: Point(n).distance(Point(104.06, 30.67))) # 计算到所有节点的最短路径 shortest = nx.single_source_dijkstra_path_length(G, start_node, weight="weight") # 把结果落到县级面上(取每个县几何中心最近的节点) county["centroid"] = county.geometry.centroid county["nearest_node"] = county["centroid"].apply( lambda p: min(G.nodes, key=lambda n: Point(n).distance(p)) ) county["dist_km"] = county["nearest_node"].map(shortest) print(county[["NAME", "dist_km"]].sort_values("dist_km").head(10))这段代码的核心是nx.single_source_dijkstra_path_length,它返回从起点到所有可达节点的最短距离。start_node的选取用了成都市中心坐标(104.06, 30.67)最近的节点。最后把每个县的几何中心映射到最近的图节点,取出对应的距离值。
需要说明的是,这个评估非常粗糙:它假设所有公路都可以双向通行,没有考虑道路等级和实际车速,也没有处理立交和断头路。但对于「哪些县离成都路网距离最近」这种排序问题,结果是有参考价值的。如果你要做更精细的分析,可以把道路等级作为权重系数乘进去,高速权重低、国道权重高,这样算出来的「时间距离」会更接近实际。
从那以后我每次拿到新的 SHP 包,都会先做三件事:检查.prj确认坐标系、用make_valid修一遍几何、把中文编码统一成 UTF-8。这三步走完,后面无论做可视化还是空间分析,基本不会再出玄学问题。希望帮到你。
本文还有配套的精品资源,点击获取