简介:面向GIS从业者、城市规划与交通研究人员及地理数据学习者,这份资源提供2022年7月山东省的矢量地理底图,可用于行政区划制图、路网可达性分析、物流选址与灾害应急路线评估等场景,帮助读者快速搭建省级空间分析的数据基础。压缩包共33个文件、约37.59MB,以shp、dbf、prj、shx、xml等格式为主:shp承载省、市、县三级行政边界及道路、铁路的几何要素,dbf保存名称、等级、里程等属性字段,prj定义坐标系统以保障定位精度,shx与xml则分别提供空间索引和元数据说明,配套齐全、可直接在ArcGIS、QGIS等软件中加载。目前已有2028人学习关注,说明其在同类数据中具备一定参考价值。读者可据此完成空间查询、叠加与缓冲区分析,并输出行政边界与交通网络专题地图,为后续研究与决策提供直观支撑。
1. 拿到一份省级 SHP 合集:山东行政区划加路网到底能干什么
上周有个做物流选址的朋友甩过来一个压缩包,说里面是山东的省、市、县三级行政边界,外加道路网和公路网,问我能不能直接喂进他那个选址模型里跑一版。我打开一看,典型的 GIS 基础数据合集:行政面要素分层清晰,路网按等级拆了图层,坐标系是常见的 CGCS2000 地理坐标。这类数据在山东行政边界、道路网、铁路网相关的 GIS 项目里属于高频刚需——做区域规划、做网点覆盖分析、做交通可达性测算,都绕不开它。
它解决的核心问题是「底图从哪来」。很多人卡在第一步:边界数据要么精度不够,要么层级混乱,路网要么断头要么属性缺失。这份合集把省、市、县三级行政单元和道路、公路网络打包在一起,省掉了东拼西凑的时间。适合谁?做区域经济分析的研究人员、做选址和路径规划的工程人员、以及需要一份干净底图做可视化的 GIS 从业者。下面我按「数据长什么样 → 怎么加载验证 → 怎么用起来 → 坑在哪」的顺序拆。
2. 拆开压缩包先看结构:图层命名、坐标系与字段设计
拿到任何一份 SHP 合集,我第一件事不是急着加载,而是先看目录结构和属性表。这一步做扎实了,后面能省掉大量返工。这份数据的组织方式比较规整,行政区和路网分开存放,每类要素一个独立图层,配套的 .shp、.shx、.dbf、.prj 四件套齐全。
2.1 图层清单与要素类型判断
行政区划部分通常按三级拆分:省级一个面图层、市级一个面图层、县级一个面图层。路网部分按道路等级或类型拆分,常见的是道路网和公路网两个线图层,有的还会单独拆出铁路网。判断要素类型最直接的办法是看 .shp 文件头,但更省事的是用 QGIS 或 ArcGIS 直接拖进去看几何类型。
我一般先用命令行快速扫一遍文件清单,确认没有缺件:
# 列出目录下所有 shp 及其配套文件,确认四件套完整 ls -lh *.shp *.shx *.dbf *.prj 2>/dev/null # 用 ogrinfo 查看每个图层的几何类型、要素数和坐标系 for f in *.shp; do echo "=== $f ===" ogrinfo -so "$f" "$(basename "$f" .shp)" done这段脚本的逻辑很直白:第一段确认每个 .shp 都有对应的 .shx 索引、.dbf 属性表和 .prj 投影文件,缺任何一个都会导致加载异常或坐标系丢失。第二段用 GDAL 的 ogrinfo 逐个读取图层元信息,-so表示只输出摘要不读要素,速度快。重点看三个字段:Geometry 是 Polygon 还是 LineString、Feature Count 要素数量是否符合预期(比如县级图层应该是山东所有区县的数量级)、以及 Layer SRS 里的坐标系定义。
如果 ogrinfo 报「Unable to open datasource」,八成是路径里有中文或空格,加引号或者换到纯英文路径下再试。这个坑我踩过不止一次。
2.2 坐标系与投影的确认
.prj 文件里写的是 WKT 格式的坐标系定义。山东的行政边界数据常见两种:CGCS2000 地理坐标系(单位是度)或者 CGCS2000 3 度带高斯投影(单位是米)。这两者差别很大——地理坐标下直接算面积会得到平方度这种没意义的单位,必须投影转换后才能做面积统计和距离测算。
确认方法:
# 直接读取 prj 文件内容 cat 县级行政区.prj # 或者用 ogrinfo 看 SRS 的 EPSG 代码 ogrinfo -so 县级行政区.shp 县级行政区 | grep -i "EPSG\|PROJCRS\|GEOGCRS"如果输出里出现GEOGCRS["China Geodetic Coordinate System 2000"]且没有PROJCRS包裹,说明是地理坐标,后续做面积和距离前必须投影。山东常用的投影是 CGCS2000 3 度带,中央经线按经度范围选,山东大致落在 117E 到 122E 之间,对应 3 度带的带号 39(中央经线 117E)或 40(中央经线 120E)。选哪个带要看数据覆盖范围,跨带的话建议统一重投影到一个带或者用 Albers 等面积投影。
提示:不要在地理坐标系下直接做 Buffer 或面积计算,结果会严重失真。这是新手最容易翻车的地方,血泪经验。
2.3 属性表字段的实用价值
行政区划图层的属性表通常包含行政区代码、名称、层级等字段。这些字段是做空间关联的钥匙——比如你想把统计数据按县挂到地图上,就得靠行政区代码做 Join。路网图层的属性表一般有道路名称、等级、类型等字段,等级字段决定了符号化方案和网络分析时的权重设置。
用 ogrinfo 看字段定义:
# 查看属性表字段名和类型 ogrinfo -so 道路网.shp 道路网 | sed -n '/Layer name/,$p'输出里会列出每个字段的名称和类型(String、Integer、Real 等)。重点确认行政区代码字段是不是字符串类型且位数完整——有的数据源会把代码存成数值,前导零丢失,导致关联失败。如果发现代码字段是 Integer 且位数不对,得在加载后用字符串补零处理。
3. 加载与验证:用 Python 把边界和路网跑通一遍
结构看清楚之后,下一步是实际加载验证。我习惯用 GeoPandas 做快速验证,因为它读 SHP 方便、做空间操作也顺手。这一章把加载、投影转换、基础空间统计串起来,跑通了就说明数据可用。
3.1 GeoPandas 读取与坐标系检查
先装环境,核心就三个包:
pip install geopandas shapely pyproj然后写个脚本把三个行政层级和路网都读进来,打印基本信息:
import geopandas as gpd # 读取各图层,注意路径按实际文件名调整 province = gpd.read_file("省级行政区.shp") city = gpd.read_file("市级行政区.shp") county = gpd.read_file("县级行政区.shp") road = gpd.read_file("道路网.shp") highway = gpd.read_file("公路网.shp") # 打印每个图层的要素数、几何类型、坐标系 for name, gdf in [("省级", province), ("市级", city), ("县级", county), ("道路", road), ("公路", highway)]: print(f"{name}: {len(gdf)} 个要素, 几何类型 {gdf.geom_type.unique()}, CRS {gdf.crs}")这段代码的关键在最后一行输出。len(gdf)给要素数,用来核对数据完整性——比如县级图层如果只有几十个要素,那大概率是只覆盖了部分地市。geom_type.unique()确认几何类型是否统一,混入 MultiPolygon 和 Polygon 是正常的,但如果出现 GeometryCollection 就得留意。gdf.crs打印坐标系,如果显示 None,说明 .prj 缺失或读取失败,必须手动指定。
参数说明:gpd.read_file默认用 Fiona 引擎,遇到编码问题(比如中文属性乱码)可以加encoding="utf-8"或encoding="gbk"试。山东的行政区名称是中文,dbf 的编码常见是 GBK,读出来乱码就换编码。
3.2 投影转换与面积统计
确认是地理坐标后,第一步做投影转换。以 CGCS2000 3 度带 40 带为例:
# 定义目标投影:CGCS2000 3 度带第 40 带,中央经线 120E target_crs = "EPSG:4547" # CGCS2000 / 3-degree Gauss-Kruger CM 120E # 转换县级图层并计算每个县的面积(平方公里) county_proj = county.to_crs(target_crs) county_proj["area_km2"] = county_proj.geometry.area / 1e6 # 按面积排序看前几个 print(county_proj[["名称", "area_km2"]].sort_values("area_km2", ascending=False).head())to_crs做投影转换,EPSG:4547 对应 CGCS2000 3 度带中央经线 120E 的投影坐标系,单位是米。geometry.area在投影坐标下算出来是平方米,除以 1e6 转平方公里。字段名「名称」要按实际属性表字段调整,如果字段是拼音或英文,换成对应名称。
这一步的验证价值在于:算出来的面积应该和公开的行政区面积量级吻合。如果某个县算出几万平方公里或者零点几平方公里,那要么投影选错了带号,要么几何有拓扑错误。我一般会挑两三个熟悉的县做交叉核对,对不上就回头查坐标系。
3.3 路网与行政边界的空间叠加
路网数据单独看没意义,得和行政区叠加才有分析价值。最常见的操作是统计每个县内的道路总里程:
# 把路网投影到同一坐标系 road_proj = road.to_crs(target_crs) # 空间叠加:找出落在每个县内的道路 # 先用空间索引加速,再逐县裁剪 county_proj = county_proj.reset_index(drop=True) road_in_county = gpd.sjoin(road_proj, county_proj[["名称", "geometry"]], how="inner", predicate="intersects") # 按县汇总道路长度(公里) road_in_county["length_km"] = road_in_county.geometry.length / 1000 result = road_in_county.groupby("名称")["length_km"].sum().sort_values(ascending=False) print(result.head(10))sjoin做空间连接,predicate="intersects"表示道路与县边界相交即算入。how="inner"只保留匹配上的记录。geometry.length在投影坐标下算出来是米,除以 1000 转公里。groupby 按县名汇总。
这里有个细节:一条跨县的道路会被拆到多个县里,intersects只判断相交不裁剪,所以长度会重复计算。要精确统计,得用overlay做裁剪:
# 精确裁剪:把道路按县界切分后再统计 road_clipped = gpd.overlay(road_proj, county_proj[["名称", "geometry"]], how="intersection") road_clipped["length_km"] = road_clipped.geometry.length / 1000 precise = road_clipped.groupby("名称")["length_km"].sum()overlay的how="intersection"会把每条道路在县界处切断,每段只归属一个县,长度统计才准确。代价是计算量大,数据量大时慢,但结果可信。两种方法我都跑一遍对比,差异大的县说明跨县道路多,值得单独看。
4. 避坑与排查:SHP 合集最容易翻车的五个地方
数据能用和用得对是两回事。这一章把我实际踩过的坑列出来,每条按现象、原因、解决三段写,照着排查能省不少时间。
4.1 中文属性乱码
现象:加载后行政区名称显示成乱码或问号。原因:.dbf 文件的编码和读取时指定的编码不一致,山东这类中文数据常见 GBK 编码,而 GeoPandas 默认按 UTF-8 读。解决:读取时显式指定encoding="gbk",如果还是乱码,用ogrinfo先确认 dbf 的实际编码,或者用 QGIS 打开看属性表是否正常,QGIS 的编码识别更宽容。实在不行用dbfread单独读 dbf 转成 UTF-8 再合并。
4.2 坐标系缺失导致位置偏移
现象:加载后图层跑到海里或者和底图对不上。原因:.prj 文件缺失或内容错误,GeoPandas 读进来 crs 为 None,后续操作全按无坐标系处理。解决:先cat看 .prj 是否存在且内容合理,缺失的话根据数据来源手动指定gdf.set_crs("EPSG:4490")(CGCS2000 地理坐标),再转投影。注意set_crs只是打标签不改变坐标值,to_crs才是真正转换,别搞混。
4.3 几何拓扑错误引发叠加失败
现象:overlay或sjoin报拓扑异常,或者结果要素数对不上。原因:面要素存在自相交、悬挂节点、重复点等拓扑错误,SHP 格式对拓扑约束不严,数据生产环节容易引入。解决:叠加前先做几何修复:
# 修复无效几何:buffer(0) 是常用的快速修复手段 county_proj["geometry"] = county_proj.geometry.buffer(0) # 检查修复后是否还有无效几何 invalid = county_proj[~county_proj.geometry.is_valid] print(f"仍有 {len(invalid)} 个无效几何")buffer(0)对大多数自相交有效,但对复杂拓扑问题可能改变几何形状,修复后要核对面积变化。更稳妥的是用shapely.make_valid,但版本要求较高。
4.4 路网断头与重复线
现象:做网络分析时路径算不通,或者长度统计偏大。原因:路网数据在采集时存在断头路(节点不连通)和重复线(同一路段存了两条)。解决:断头路用shapely.ops.linemerge合并相邻线段,再用unary_union做节点吸附;重复线用drop_duplicates按几何 WKB 去重,或者设一个距离容差做缓冲区去重。这一步没有银弹,得结合具体数据看,我一般先可视化抽查几个区域再决定处理策略。
4.5 行政区代码前导零丢失
现象:按行政区代码做表关联时大量匹配不上。原因:代码字段被存成数值类型,前导零被吃掉,比如「370102」变成「370102」看着没变,但「010102」这类就会丢零。山东的区县代码都是六位,首位是 3,理论上不丢零,但如果数据经过多次转换,字段类型可能被改。解决:加载后统一转字符串并补零:
# 行政区代码统一为六位字符串 county["代码"] = county["代码"].astype(str).str.zfill(6)zfill(6)左补零到六位,确保关联键一致。这个操作在做统计数据和空间数据挂接时是标配,别省。
5. 进阶用法:把路网做成可达性分析的基础网络
数据验证通过、坑排完,最后落到一个具体技巧上:用这份路网做简单的可达性测算。这是路网数据最高频的进阶用法,也是检验数据质量的试金石。
5.1 构建网络数据集与最短路径
用 NetworkX 把路网转成图结构,节点是道路端点,边是路段,权重用长度:
import networkx as nx from shapely.geometry import LineString # 从路网构建无向图,边权重为路段长度(米) G = nx.Graph() for _, row in road_proj.iterrows(): geom = row.geometry if geom.geom_type == "LineString": coords = list(geom.coords) # 用首尾点作为节点标识,实际项目建议做节点吸附 start, end = coords[0], coords[-1] G.add_edge(start, end, weight=geom.length) print(f"节点数 {G.number_of_nodes()}, 边数 {G.number_of_edges()}")这段代码是简化版,直接用线段首尾坐标当节点。实际项目里必须做节点吸附——把距离在容差内的端点合并成同一节点,否则图是断开的,最短路径算不出来。吸附可以用shapely.ops.snap或者自己写 KDTree 近邻合并。weight=geom.length用路段长度做权重,也可以换成通行时间(长度除以等级对应的速度)。
5.2 从县城到地级市的可达性测算
有了图,就能算任意两点间的最短路径。比如算某县到所属地级市的路网距离:
# 找最近的节点作为起终点(简化处理,实际用空间索引) def nearest_node(G, point): return min(G.nodes, key=lambda n: (n[0]-point.x)**2 + (n[1]-point.y)**2) # 取某县几何中心和某市几何中心 county_center = county_proj[county_proj["名称"] == "某县"].geometry.centroid.iloc[0] city_center = city_proj[city_proj["名称"] == "某市"].geometry.centroid.iloc[0] src = nearest_node(G, county_center) dst = nearest_node(G, city_center) try: dist = nx.shortest_path_length(G, src, dst, weight="weight") print(f"路网距离约 {dist/1000:.1f} 公里") except nx.NetworkXNoPath: print("两点不连通,检查路网断头或节点吸附")nearest_node用欧氏距离找最近节点,数据量大时换成scipy.spatial.cKDTree加速。shortest_path_length按权重求最短路径,NetworkXNoPath异常说明图不连通,回头查 4.4 的断头路问题。算出来的距离和实际驾车距离对比,偏差在合理范围内就说明路网质量可用。
5.3 一个我坚持的习惯
做可达性分析时,我每次都会先抽十条已知路线做人工核对——挑几个熟悉的县到市、市到市的组合,把算出来的路网距离和导航软件的实际距离比一遍。偏差超过 20% 就说明路网有系统性缺失或者节点吸附没做好,这时候算出来的所有结果都不可信。这个习惯是从一次翻车里来的:当时直接跑全量可达性,结果出来发现某几个县的距离明显偏大,回头查才发现那片区域的路网图层漏了一段高速,导致路径绕行。从那以后我每次做网络分析前都强制走一遍抽样核对,宁可多花半小时,也不让错误结果流到下游。
这份山东行政区划加路网的 SHP 合集,结构规整、层级清晰,作为区域分析和可达性测算的底图是够用的。关键是把坐标系确认、拓扑修复、节点吸附这几步做扎实,剩下的就是按业务需求往上叠分析逻辑。希望帮到你。
本文还有配套的精品资源,点击获取