☰
全国矢量地图shp数据实战:坐标系检查、属性清洗与转换避坑指南
2026/9/26 8:40:32 网站建设 项目流程

简介:全国矢量地图shp格式压缩包,面向GIS学习者、规划师、测绘地信从业者,提供可直接使用的全国基础地理底图。包内共85个文件,以28套shp、shx、dbf三件套为主,附带一个xml元数据文件;其中shp保存点、线、面几何对象,shx用于建立空间索引,dbf存储行政区划名称、道路等级、水体类型等属性信息,覆盖省级到县级行政区划、各级道路网络、河流湖泊水系、地形地貌、建筑物及生态保护区等常见图层。压缩包整体34.27MB,文件按目录组织,可单独加载任一图层,也能统一导入ArcGIS、QGIS等平台进行投影转换、缓冲区分析、专题出图。目前已有543人学习浏览,使用这份数据可以省去地图数据的收集、格式转换与初步配准工作,直接聚焦于空间分析和成果表达,适用于城市体检、资源环境评价、交通可达性分析等具体业务场景。

1. 全国矢量地图 shp 数据到底解决什么问题:三个高频场景

干 GIS 这行,最不缺的就是“自己画”,最缺的是“现成的全国底图”。不管是做规划分析、出专题图,还是给前端项目导一份省界,临时去爬高德矢量、东拼西凑边界,最后多半会栽在坐标系不一致、属性字段千奇百怪、图层压盖这些问题上。全国矢量地图 shp 格式这份打包资源,就是把“全国行政区划边界+常用基础图层”整理成一个开箱即用的数据集,省去到处找边界描边的过程。它适合这样的读者:要么是刚开始接触 arcgis/QGIS 的新手,需要一个现成 shp 来练手;要么是需要做全国尺度分析、出图、做数据二次开发的从业者。下面按我自己的使用流程把它拆开讲。

2. 解压后的第一遍体检:图层、坐标系与编码验证,别急着用

2.1 压缩包里的 shp 文件完整形态与识别

shp 不是单文件,而是一组同名文件。很多人第一次拿到 rar 解压后只看到.shp,直接拖进 arcgis,结果提示缺少投影文件或打不开属性表,问题往往出在配套文件没解压干净。一个完整的 shapefile 至少要有.shp(几何)、.shx(索引)、.dbf(属性表),常常还带.prj(投影)、.cpg(编码)、.sbn/.sbx(空间索引)。这个压缩包里如果每个图层都是这么一组文件,那才算真的“建好了库”。

拿到压缩包后,我的习惯是先列一遍目录,不急着加载。用命令行做一次结构体检比用 arcgis 手工点一遍快得多,也更容易发现漏文件:

ls -lh 全国矢量地图/ find 全国矢量地图/ -name "*.shp" | wc -l find 全国矢量地图/ -name "*.prj" | wc -l

第一条命令看每个图层大小,第二条统计 shp 数量,第三条统计投影文件数量。如果 shp 数量和 prj 数量差了太多,说明一部分图层缺投影参数,后面做叠加分析时会被不同坐标系搞得晕头转向。常见的全国图层包里有省级面、市级面、县级点、国界线、省界线、主要河流、高速路网这类要素,每个图层的几何类型和属性字段各有差异,所以体检时不光看文件齐不齐,还要看几何类型对不对。

2.2 用 ogrinfo 与 geopandas 检查坐标系和范围

在不知道数据坐标系之前,任何空间操作都是危险的。全国尺度数据常见的坐标系有 CGCS2000 经纬度、WGS84 经纬度,也有被投影成 Albers 等面积投影的版本。如果图层没有.prj,或者 prj 里写的是“GCS_Beijing_1954”,你却以为它是 WGS84,叠加底图时就会整整偏移几百米到几公里。

用 GDAL 自带的 ogrinfo 做摘要检查:

ogrinfo -so 全国省级行政区.shp 全国省级行政区

-so表示只输出摘要信息。它会列出图层名、要素数量、几何类型、坐标范围,最重要的是输出 Spatial Reference 那一行,直接确认投影是什么。如果显示的是GEOGCS["WGS 84"...],说明是经纬度;如果出现PROJCS,那就是投影坐标系,后面做面积计算或转瓦片时要留意。

用 Python 再做一次更细的检查,适合要写批处理脚本的情况:

import geopandas as gpd gdf = gpd.read_file('全国省级行政区.shp', encoding='utf-8') print("坐标系:", gdf.crs) print("范围:", gdf.total_bounds) # 输出左下角和右上角经纬度 print("要素数:", len(gdf)) print("几何类型分布:") print(gdf.geom_type.value_counts())

这段代码有四个输出点,作用分别是指认坐标系、看数据覆盖范围、确认要素数量、排查是否存在杂混几何类型。比如一个“全国面图层”里混进了两条线要素,用geom_type.value_counts()一眼就能发现,后续做空间连接时不会因为几何类型不一致而静默出错。

2.3 属性表中文乱码与字段名截断问题

全国 shp 数据的属性表通常存省市名称、行政区划代码 PAC、面积等字段。老数据来源复杂,编码常常不统一:有的是 GBK,有的是 GB18030,少数是 UTF-8。arcgis 默认按系统区域猜编码,遇到.cpg文件缺失或内容不对,中文名就会变成一坨问号、乱码。QGIS 在打开时如果检测不到编码,也会让你手选,这时去乱猜就浪费时间了。

最快的办法是看.cpg文件内容,再去确认.dbf的编码头:

xxd 全国省级行政区.dbf | head -3 file 全国省级行政区.dbf

file命令会输出类似 “dBase III DBase” 的信息,而xxd查看前几个字节,能看到字段名区域是 ASCII 还是高位中文,经验上中文高位字节出现时基本就是 GBK。确认后,在 QGIS 里按“图层属性 → 数据源 → 编码手动设为 GBK”即可恢复;用 Python 读的时候则把encoding='utf-8'改成encoding='gbk':

gdf = gpd.read_file('全国省级行政区.shp', encoding='gbk')

这里必须强调一个容易误判的点:乱码不一定是文件坏了,多半是读法不对。数据本身还在,换编码重读一遍通常就恢复。

平时我自己维护这类数据集时,会顺手统一做一次编码转换,把 dbf 转成 UTF-8,避免每次打开都手动指定。具体做法是先把属性导出为 CSV,再重新生成 shp;也可以直接用ogr2ogr配合-lco ENCODING=UTF-8重写一份。重写前先备份,毕竟 re-encode 这种事翻车率不低,字段长度和特殊符号都可能被截断。

3. 提取目标区域:行政区划过滤与矢量裁剪的实用参数

3.1 按属性字段精确提取省市范围

全国图层范围大,动辄几十万到上百万个面要素,直接拿来做局部城市分析非常臃肿。最常见的需求就是“我要北京市的范围”或者“我要河北省所有县级行政区”。这种提取用属性查询比空间裁剪稳定,因为属性过滤不会产生新几何,速度也快得多。

在 arcgis 里是先按属性选择再导出数据,命令行里用 ogr2ogr 一步完成:

ogr2ogr -f "ESRI Shapefile" 北京市.shp 全国省级行政区.shp \ -where "NAME='北京市'"

-where是 SQL 表达式,这里用的是字段名NAME,具体字段以图层属性为准,可能是省、NAME、PAC等。如果字段值是中文,注意终端能正确传 UTF-8 参数;在 Windows cmd 下建议先确认代码页,或者把 SQL 写进.sql文件用-sql参数引用,避免编码踩坑。

提取出来的北京市.shp 会保留原属性字段,但不会保留原图层的符号化配置。如果你接下来要拿去出图,需要重新配置一遍样式;如果只是做数据裁剪和计算,那就直接可用。

按多个省提取时,IN语法比多个OR更清晰:

ogr2ogr -f "ESRI Shapefile" 京津冀.shp 全国省级行政区.shp \ -where "NAME IN ('北京市','天津市','河北省')"

这段命令的逻辑是:从全国图层筛选出三个省级行政区,合并写入一个新的 shp。注意这里的合并只是把要素拷到一起,没有做拓扑融合;如果三块边界紧密相连,仍会保留公共边,面之间没有真正融合。

3.2 用矩形范围裁剪:把大图层切成工作区域

没有属性字段可选时,就要靠坐标范围来做空间筛选。比如做水文分析只关心塔里木河流域,或者做项目只想要某个经纬度范围内的路网,矩形裁剪是最省力气的方式。

GDAL 的-spat参数直接按空间范围过滤:

ogr2ogr -f "ESRI Shapefile" 南盘江流域.shp 全国河流.shp \ -spat 103.5 24.5 106.5 26.5

-spat后面依次是左下角 X、左下角 Y、右上角 X、右上角 Y,单位随源数据坐标系。如果源数据是经纬度,这里就是经纬度;如果源数据是投影坐标,一定要用投影坐标值,否则会裁出一个空图层。拿不准源坐标系时,先回第 2 章用ogrinfo -so确认。

这种裁剪的坑在于:它只按要素范围是否与外框相交来判断,所以落在边界上的线、面会有部分残留在结果里,导出的要素几何没有被真正“切开”。想做标准裁剪,比如只保留河流完全进入流域内的部分,必须用真正的几何裁剪函数。

用 geopandas 做真正的几何相交裁剪:

import geopandas as gpd from shapely.geometry import box river = gpd.read_file('全国河流.shp', encoding='utf-8') aoi = box(103.5, 24.5, 106.5, 26.5) # 左、下、右、上 river_crs = river.to_crs(aoi.crs if hasattr(aoi, 'crs') else river.crs) clipped = gpd.clip(river, gpd.GeoDataFrame(geometry=[aoi], crs=river.crs)) clipped.to_file('南盘江流域.shp', encoding='utf-8')

这里用gpd.clip会把每个要素与aoi做真正的几何相交,切割后的边界干净,面积量算也更准。如果只是筛要素而不切割,直接用索引river.cx[103.5:106.5, 24.5:26.5]就能得到候选要素,但结果几何不保证都在框内。

3.3 如何在 shp 图中去掉一部分矢量:反向选择与擦除

日常需求里还有一种相反操作:不要某个区域,保留其余部分。比如做全国专题图时想去掉台湾省以外的海域界线,或者在省级图上挖掉某个县做“空白区”展示。术语上叫“擦除”或“反向裁剪”。

ogr2ogr 没有直接的擦除参数,常见做法是先选中要排除的要素,再反选导出。用 arcgis 是“按属性选择 → 切换选择 → 导出数据”;命令行里可以用 SQL 的NOT IN:

ogr2ogr -f "ESRI Shapefile" 全国除北京.shp 全国省级行政区.shp \ -where "NAME NOT IN ('北京市')"

逻辑上可以对比 SQL 条件做排除,但要注意字段值里可能带空格或全角字符,比如 “北京市 ” 后面多了一个空格,导致排除失败。排查时要先用属性表看真实字段值,必要时用TRIM(NAME)或者在 SQL 里加LIKE '%北京%':

ogr2ogr -f "ESRI Shapefile" 全国除北京.shp 全国省级行政区.shp \ -where "NAME NOT LIKE '%北京%'"

这种写法会连“北京市”“北京首都机场边界”一类都排掉,如果只想排除一个市,务必确认字段取值唯一。空间上的擦除则需要用overlay_difference或 arcgis 的 Erase 工具,它按几何区域删除,而不是按属性行删除。

4. 格式转换实战:shp 与 kml、geojson、dwg、3dtiles 互转的边界场景

4.1 shp 转 GeoJSON 与 KML:线上可视化最常用

目录服务、Web 地图、给前端同事传数据,几乎都绕不开 GeoJSON。shp 转 GeoJSON 的核心不是格式本身,而是坐标系的转换。源数据如果是投影坐标系,要先转为 WGS84 经纬度,否则浏览器里的地图会把要素画到海里。

一条命令搞定转换和投影:

ogr2ogr -f GeoJSON 全国省级行政区.geojson 全国省级行政区.shp \ -t_srs EPSG:4326 \ -lco WRITE_BBOX=YES

-t_srs EPSG:4326强制输出 WGS84 经纬度,WRITE_BBOX=YES会在每个要素的 properties 里带上包围盒,这个属性对前端做视野裁剪很有用。如果源数据字段名很长,GeoJSON 里一般不受限,但注意 shp 的 dbf 字段名最长 10 字节,所以中文字段名基本都会出问题,建议转换前就把字段重命名为拼音或英文。

KML 是 Google Earth 系和无人机航测软件常用的格式。shp 转 KML 时最容易丢的是属性字段,Google Earth 默认只显示名称,其他字段得写进扩展数据里才能显示:

ogr2ogr -f KML 输出.kml 全国省级行政区.shp \ -dsco NameField=NAME \ -dsco DescriptionField=FULLNAME

NameField和DescriptionField用来指定 KML 里弹出气泡的名称和描述字段。如果省略这两个参数,KML 里可能只剩一个空名字,到了 Google Earth 中所有省份都显示为“未命名”,又得重新编辑,费时费力。

4.2 dwg 转 shp:先过 dxf 中转站

dwg 转 shp 是很多规划、土木背景用户的高频需求,因为原始 CAD 图纸是 dwg,而分析软件只认 shp。GDAL 官方并没有稳定读 dwg 的驱动,这类转换的常规路径是先另存为 dxf,再由 ogr2ogr 读取 dxf 转 shp。

# 核对 dxf 能被识别 ogrinfo -so 规划用地.dxf # dxf 转 shp,按图层提取 ogr2ogr -f "ESRI Shapefile" 规划用地.shp 规划用地.dxf \ -where "Layer='JMD'" \ -nlt MULTILINESTRING

关键点是-where "Layer='JMD'",dxf 文件里不同 CAD 图层对应不同业务语义,路面、用地面、建筑轮廓全挤在一个 dxf 文件里,不按 Layer 过滤会全混在一起。-nlt MULTILINESTRING是为了防止 CAD 里大量短线要素被揉成多部件线,后续清理更麻烦。

转完后的 shp 往往会带着一堆零碎短线、重复线,这是在 CAD 里画图不规范的后果,不是格式转换本身造成的。我一般会在转换后用v.clean(Grass GIS 工具)或 QGIS 的“修复几何”跑一遍,把重复线段、微小缝隙清掉,再进入下一步分析。

4.3 shp 转 3dtiles 与瓦片:精度、层级与性能取舍

全国底图直接转 3dtiles 会卡到怀疑人生。这个转换的本质是把矢量面覆盖到地形上做切片,不是简单换个后缀。我见过有人拿着百度爬下来的全国 shp 直接去导 3dtiles,数据动辄几十万个面,生成到一半内存就爆了。

常用工具是 CesiumLab 和国产的 Model Lab 系列,它们把 shp 作为数据源,设定最大层级和高度模式后输出。决定成败的更多是准备工作而不是工具:

  • 先把属性表压缩到只剩必要字段,3dtiles 的每个要素都会携带属性,字段越多包越大
  • 坐标系必须转成 EPSG:4326 或 EPSG:4978,Cesium 不支持任意投影
  • 如果面要素数量超过一万个,建议先做简化,比如用Simplify工具抽稀边界点,保留层级低时不需要的细节
  • 面数据的“高度”模式要决定是贴地还是拉伸,贴地时一定要关掉默认的height拉伸值,否则整个城市像起了个高台

从瓦片角度讲,shp 本身不适合发布层级过多的瓦片。数据分辨率只有 1:100 万级别时,切到第 12 级已经能明显看到边界锯齿,再往上切只会放大噪声,不如在切瓦片前先按比例尺做一层综合简化。

4.4 反方向:geojson、kml 转回 shp 时的属性保障

现在越来越多的开源数据以 GeoJSON 发布,但 arcgis 老版本和多数测绘软件仍要求 shp。从 GeoJSON 转回 shp 时,JSON 里的嵌套属性在 dbf 里存不下,会被直接丢掉。这是因为 dbf 是扁平表结构,GeoJSON 的 properties 可以是任意嵌套对象。

处理方式是先把嵌套属性摊平,再转出去:

ogr2ogr -f "ESRI Shapefile" 输出.shp 输入.geojson \ -lco ENCODING=UTF-8 \ -nlt PROMOTE_TO_MULTI

PROMOTE_TO_MULTI是另一个预防措施。GeoJSON 里的单个 Polygon 和 MultiPolygon 可以混在同一文件里,而 shp 不推荐混合几何类型,PROMOTE_TO_MULTI会把 Polygon 提升成 MultiPolygon,保证整个图层几何类型统一。

KML 转 shp 要注意的还有:KML 的坐标是经纬度和海拔,shp 里没有 Z 值时会丢弃海拔,线状地物还好,如果是地形剖面类数据,转完后高程信息会消失。遇到这种需求,我会改用 GeoPackage 或 FileGDB 作为目标格式,以保留三维信息。

5. shp 文件使用避坑:五个高频翻车现象与修复记录

5.1 打开图层时属性表中文全是乱码

现象:arcgis 打开全国 shp 属性表,省市名称变成“锟斤拷”“烫烫烫”一类乱码,QGIS 里则是“���”符号。

原因:绝大多数是全国数据在制作时按 GB2312/GBK 保存 dbf,而读取方默认按 UTF-8 或系统区域码解析。.cpg文件要么缺失,要么内容写错了编码名。

解决:先查看.cpg内容,例如cat 图层.cpg显示 “OEM” 或 “936” 即 GBK;然后在 QGIS 图层属性里把编码手工改成 GBK 重新加载。如果用 Python 读,把 encoding 参数改为'gbk'。代码上可以把 shp 重新导出一次,让新的.cpg写上UTF-8,以后再开就不会有问题。

5.2 dbf 字段名被截断成 10 个字符

现象:属性表里字段名明明叫POPULATION_2024,导出的 shp 却显示成POPULATIO_2,甚至变成POP_2024。

原因:shapefile 的 dbf 第三版规范里,字段名最多 10 字节。中文一个字符占 2 字节或 3 字节,更容易被截断。GeoJSON 转 shp、arcgis 导出时都会遇到。

解决:建字段时用 <=10 字节的英文名,比如POP24而不是人口_2024。如果已经截断了,在 arcgis 里用“字段计算器”另建一个新字段,把截断字段的值复制过去。命令行里提前重命名字段最省事,用ogr2ogr -sql重建字段序列。

5.3 shp 转 KML 后 Google Earth 里只剩轮廓没有名称

现象:所有省界、区界都显示成了多边形,但点击要素看不到任何属性名称,显示的都是“未命名”或空白。

原因:KML 的显示名称和描述字段是单独定义的,ogr2ogr 不会自动猜哪个属性当名称,需要-dsco NameField显式指定。很多教程里不带这个参数,总以为属性会自动带过去。

解决:转换命令里加:

ogr2ogr -f KML 输出.kml 全国省级行政区.shp -dsco NameField=NAME

如果字段名不是 NAME,换成实际字段名。一次转换里只能指定一个名称字段,多个字段可以在DescriptionField里拼。

5.4 省界线文件“省1”“省2”到底有什么区别,接边时对不上

现象:资料包里有两个 shp,一个叫“省界线_省1”,一个叫“省界线_省2”,看起来都是边界线,叠到一起却有一边缝隙、一边重叠。再做拓扑查错,边对不齐。

原因:常见命名里“省1”多数是省级行政单位的面边界线,即 polygon 转成的线;“省2”可能是国界线(含海岸线)或另一种数据比例的边线,两者的精度、抽取规则、拓扑容差都不同。一个严格贴着省界面,一个则按国界测绘成果单独整理,所以天然有差异。

解决:当作数据源时先确认用途。做省级统计用面图层就够了,不需要线图层;做国界专题图时要把国界线图层与省界线做一次“对齐”处理,先叠加,以高精度图层为准,用snap或v.clean处理缝隙。千万不要把“省1”“省2”随意合并,它们不一定出自同一版本数据库。

5.5 几何损坏:arcgis 报“无法读取 shapefile”或 shapechk 报错

现象:某个 shp 加载时 arcgis 提示“不能读取 shapefile”,但 QGIS 却能勉强打开一部分;重复执行某次裁剪后,输出 shp 打开时要素数量对不上。

原因:shp 文件因为网络传输中断、解压软件强制中止、或者程序异常退出,导致.shx索引与.shp里的几何记录不一致。最常见是.shx没写完,而形状记录本身还在。QGIS 容错性稍强,arcgis 对索引完整度更敏感。

解决:先用 shapechk 工具诊断和修复。shapechk 是开源修复工具,它会扫描几何记录并重建索引。用法是:

shapechk 全国省级行政区.shp /a

/a表示在修复前先做分析并输出报告,报告会指出哪一段几何数据损坏。修复前一定要把原文件备份,shapechk 有时会把损坏处直接标记为无效并去掉某些要素,如果原文件还有更优恢复手段,被它先改了反而麻烦。修复完成后用ogrinfo -so再跑一遍,确认要素数符合预期再继续分析。

日常避免这种问题的习惯也很重要:rar 解压时不勾选“后台解压”,解压过程中不要开其他大软件;传输 shp 前最好打成 zip 而不是单独发.shp,避免邮箱或聊天工具吞掉.shx。

6. 进阶技巧:用渔网分割全国图层做空间统计的关键参数

拿到全国面图层却不做统计分析,等于只用了它 30% 的价值。我常做的一个操作是把全国按经纬度或投影坐标切成渔网网格,再统计每个网格覆盖的县级行政区数量或河流长度。这类需求在热词里总被叫“渔网分割 shp”,但这里有个概念要澄清:渔网任务其实是“生成渔网并叠加”,不是把 shp 本身切开。

用 QGIS 生成渔网非常直观:“矢量 → 研究工具 → 创建渔网”。关键参数是网格间距和范围。如果全国图层是经纬度坐标,1 度网格大约相当于 111 公里,适合做省级对比;如果是投影坐标或者你要做县域统计,网格间距用 100000 米至 500000 米(100 至 500 公里)更合理。网格范围建议直接选择“全国省级行政区”图层的范围,不用手动填最小坐标,避免渔网覆盖区域和图层范围出现偏差。

网格生成后做空间连接,统计每个网格与省级面的交集面积占比,才能算出“该网格主要属于哪个省”:

import geopandas as gpd grid = gpd.read_file('渔网_100km.shp', encoding='utf-8') prov = gpd.read_file('全国省级行政区.shp', encoding='utf-8') joined = gpd.overlay(grid, prov, how='intersection') joined['area'] = joined.geometry.area result = joined.groupby('grid_id').apply( lambda g: g.loc[g['area'].idxmax(), '省份'] )

这段代码的精髓是:先用 overlay 求每个网格与省份的相交面,再按grid_id分组,保留面积最大那个省份作为归属。如果不做面积比较,网格只要擦到省界边缘就会被统计成跨省,结果里会冒出来一堆“既是河南又是山东”的噪声网格。

同样逻辑可以做“每个网格内河流长度”,把线要素与网格相交后统计线段长度总和:

river = gpd.read_file('全国河流.shp', encoding='utf-8') river['len'] = river.geometry.length grid_len = gpd.overlay(river, grid, how='intersection') grid_len['seg_len'] = grid_len.geometry.length summary = grid_len.groupby('grid_id')['seg_len'].sum()

注意这里必须先求相交,再按网格 id 汇总长度,不能直接对原始river的 length 做分组,因为一条河流会跨多个网格,原始 length 是整条河的长度,与网格没有关系。初次做这个统计时我没意识到这一点,结果一个网格的河长等于整条黄河的长度,图表上出现巨大异常值;从那以后我每次做渔网统计都强制走一遍“先相交、再分组汇总”的流程,宁可多算一步,也不让脏统计混进结果。希望这一节的操作细节能帮你少走同样弯路。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询