
简介面向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 引擎遇到编码问题比如中文属性乱码可以加encodingutf-8或encodinggbk试。山东的行政区名称是中文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, ascendingFalse).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(dropTrue) road_in_county gpd.sjoin(road_proj, county_proj[[名称, geometry]], howinner, predicateintersects) # 按县汇总道路长度公里 road_in_county[length_km] road_in_county.geometry.length / 1000 result road_in_county.groupby(名称)[length_km].sum().sort_values(ascendingFalse) print(result.head(10))sjoin做空间连接predicateintersects表示道路与县边界相交即算入。howinner只保留匹配上的记录。geometry.length在投影坐标下算出来是米除以 1000 转公里。groupby 按县名汇总。这里有个细节一条跨县的道路会被拆到多个县里intersects只判断相交不裁剪所以长度会重复计算。要精确统计得用overlay做裁剪# 精确裁剪把道路按县界切分后再统计 road_clipped gpd.overlay(road_proj, county_proj[[名称, geometry]], howintersection) road_clipped[length_km] road_clipped.geometry.length / 1000 precise road_clipped.groupby(名称)[length_km].sum()overlay的howintersection会把每条道路在县界处切断每段只归属一个县长度统计才准确。代价是计算量大数据量大时慢但结果可信。两种方法我都跑一遍对比差异大的县说明跨县道路多值得单独看。4. 避坑与排查SHP 合集最容易翻车的五个地方数据能用和用得对是两回事。这一章把我实际踩过的坑列出来每条按现象、原因、解决三段写照着排查能省不少时间。4.1 中文属性乱码现象加载后行政区名称显示成乱码或问号。原因.dbf 文件的编码和读取时指定的编码不一致山东这类中文数据常见 GBK 编码而 GeoPandas 默认按 UTF-8 读。解决读取时显式指定encodinggbk如果还是乱码用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, weightgeom.length) print(f节点数 {G.number_of_nodes()}, 边数 {G.number_of_edges()})这段代码是简化版直接用线段首尾坐标当节点。实际项目里必须做节点吸附——把距离在容差内的端点合并成同一节点否则图是断开的最短路径算不出来。吸附可以用shapely.ops.snap或者自己写 KDTree 近邻合并。weightgeom.length用路段长度做权重也可以换成通行时间长度除以等级对应的速度。5.2 从县城到地级市的可达性测算有了图就能算任意两点间的最短路径。比如算某县到所属地级市的路网距离# 找最近的节点作为起终点简化处理实际用空间索引 def nearest_node(G, point): return min(G.nodes, keylambda 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, weightweight) print(f路网距离约 {dist/1000:.1f} 公里) except nx.NetworkXNoPath: print(两点不连通检查路网断头或节点吸附)nearest_node用欧氏距离找最近节点数据量大时换成scipy.spatial.cKDTree加速。shortest_path_length按权重求最短路径NetworkXNoPath异常说明图不连通回头查 4.4 的断头路问题。算出来的距离和实际驾车距离对比偏差在合理范围内就说明路网质量可用。5.3 一个我坚持的习惯做可达性分析时我每次都会先抽十条已知路线做人工核对——挑几个熟悉的县到市、市到市的组合把算出来的路网距离和导航软件的实际距离比一遍。偏差超过 20% 就说明路网有系统性缺失或者节点吸附没做好这时候算出来的所有结果都不可信。这个习惯是从一次翻车里来的当时直接跑全量可达性结果出来发现某几个县的距离明显偏大回头查才发现那片区域的路网图层漏了一段高速导致路径绕行。从那以后我每次做网络分析前都强制走一遍抽样核对宁可多花半小时也不让错误结果流到下游。这份山东行政区划加路网的 SHP 合集结构规整、层级清晰作为区域分析和可达性测算的底图是够用的。关键是把坐标系确认、拓扑修复、节点吸附这几步做扎实剩下的就是按业务需求往上叠分析逻辑。希望帮到你。本文还有配套的精品资源点击获取