简介:面向GIS分析与制图的江苏省基础地理数据包,内容包含省级、地市级、区县级三级行政区划边界,以及道路网与铁路网线状要素,可直接服务于地理信息、城乡规划、交通物流等领域的空间分析与制图需求,适合在校学生、科研人员及行业工程师快速取用。压缩包共33个文件,其中5个shp为核心要素文件,配套dbf属性数据、prj坐标系定义、shx要素索引,另含xml元数据及sbn/sbx空间索引,整体大小约41.72MB,结构规整,导入ArcGIS、QGIS等常用平台即可识别使用。该数据包已有1965人学习下载,得到较多GIS使用者的关注与使用。行政区划边界按省、市、县分层存储,可灵活匹配不同尺度下的地图表达、统计制图与区域分析;道路与铁路网络数据既可单独提取,也能与边界叠加,为交通可达性、城市群联系和用地评价等研究提供基础数据支撑。经过整理的图层名称清晰、字段规整,能大幅减少前期数据搜集与清洗时间,是开展区域研究或教学演示时的实用数据资产。
1. 这份2022.07版江苏省行政区划与路网SHP,能帮你少走多少弯路
拿到一份标注2022.07版的江苏省行政区划SHP文件时,我第一反应不是急着拖进GIS,而是先看它包含哪几类图层。这类基础数据包最常见的用途就两类:画图打底,空间分析取数。省、市、县三级行政面解决“边界在哪”的问题,道路网和公路网解决“线往哪走”的问题,两者合在一起才能做按县路网里程统计、区域道路密度分析一类的基础计算。谁需要它?做规划现状图、写评估报告、做GIS系统数据底座的开发者、数据处理人员和地图制图员都用得上。
这个版本的时间点很关键。道路每年都在变,2022年07月的路网数据,做2023年以前的现状评价基本够用;如果拿来做最新的路网规划底图,就需要先核对重点路段的增补情况。换句话说,这份资源的价值在于“底图基线”而不是“实时路况”,把它当作一套内容完整、图层分离的矢量档案来使用,比当成万能更新包要稳妥得多。
2. 拆开数据包:SHP文件“三件套”和它的五个图层组织逻辑
很多新手拿到一个名为jiangsu_2022_07的下载文件夹,习惯性双击里面最大的.shp文件就往QGIS里拖,结果要么打不开,要么属性表全乱码。这和SHP文件本身的结构有关——它从来不是“一个文件”,而是一组配套文件的集合。
2.1 一套SHP到底由哪些文件组成
一个合格的Shapefile数据包,至少会包含以下后缀文件。下载后先不要急着解压到桌面,建议在资源管理器里切换到“详细信息”视图,看看文件后缀是否齐全。
| 后缀 | 作用 | 缺失时会出现什么问题 |
|---|---|---|
| .shp | 几何信息,点线面坐标本体 | 缺它什么都做不了 |
| .shx | 几何索引,记录坐标偏移量 | 部分软件能读,但会卡或报错 |
| .dbf | 属性表,字段与属性数据都在这里 | 看不到属性内容 |
| .prj | 投影与坐标系描述文件 | 坐标系变成“未知”,分析结果容易失真 |
| .cpg | 属性编码声明,常见UTF-8或GBK | 中文属性名和内容可能乱码 |
| .sbn / .sbx | 空间索引(可选) | 不影响读取,只影响检索效率 |
这个包如果从标题看,至少包含省级行政面、市级行政面、县级行政面、道路网、公路网五类内容,实际文件数量通常还要翻倍,因为每个图层都会按上面的后缀各出一份文件。常见做法是解压后先按“电子地图数据”“行政区划”“路网”建三个子目录,再按维度分层放,避免后缀和空间数据混在一起。
我一般会用一行命令快速扫一遍数据包里的文件情况:
ls -R data/ | grep -E "\.(shp|dbf|prj|cpg)$" | head -50这条命令的作用是把data目录下所有Shapefile相关的文件列出来。-R是递归遍历子目录,grep -E按文件后缀过滤,head -50限制输出条数。执行后重点看两件事:每个图层是否都有配套的.dbf和.prj;多个时期的同名图层是否同时存在于不同子目录,避免自己在分析时误用旧版本。
2.2 五个图层怎么组织:面是底、线是网
从使用逻辑看,省、市、县三级行政面是“底”,道路网和公路网是“网”。省级面主要用来限定研究范围,市级面用于做区域对比,县级面则是大多数统计单元——要素数量适中,匹配统计年鉴很方便,也适合做聚合分析。
道路网和公路网的关系要特别留意。按行业惯例,“道路网”是综合类图层,覆盖高速、国道、省道、县道和乡道等不同等级道路;而单独的“公路网”可能是只含国省道的子集,也可能按高速公路、一级公路、二级公路分了字段。拿到数据后先打开属性表看看有没有CLASS、TYPE或ROAD_NAME这类字段,分清图层语义,否则统计“公路里程”时很容易把城市道路一起算进去。
快速查看一个图层的要素数量和坐标范围,不需要打开GUI,用GDAL自带的ogrinfo就够了:
ogrinfo -so -al data/road/road_2022.shp-so是“summary only”,只输出概要信息;-al表示遍历该数据源所有图层。跑完会显示要素数量、几何类型(Point/LineString/MultiPolygon)和四个角的最小包围矩形坐标范围。看到坐标范围是经纬度小数(比如119°、121°),就知道当前数据是地理坐标系;如果看到X坐标是几千万级别的七位数字,那基本能确定是高斯投影或Web墨卡托。这一步花不到10秒,能避免后期一大堆坐标系导致的单位错误。
2.3 打开前必查的三个字段和坐标系约定
不管哪个环节要用这份SHP,都要先确认三个信息:行政区划代码字段是否存在、道路等级字段是否完整、坐标系是否符合目标投影。
行政区划数据通常带ADCODE或XZQHBM字段,存的是六位数字编码,前两位是省级,中间两位是地级市,后两位是区县。这个编码是做属性关联的唯一主键,比手工去比对汉字段落靠谱得多。县级面的编码是唯一的,市级的编码需要省市级特殊规则,部分平台会以“+0000”补齐。
坐标系是SHP数据包里最容易被当成“玄学”的东西。发布机构不同,数据可能是CGCS2000、WGS84或西安80坐标系。同样是“江苏全省范围”,经纬度坐标和投影坐标的数值差异巨大。快速判断方法就是打开.prj文件用记事本看一眼,或者在QGIS里双击图层查看CRS标识。国内2022年的标准成果数据通常优先采用CGCS2000,但也不排除部分历史路网数据沿用WGS84。
| 坐标系 | 常见EPSG代码 | 单位特征 | 适用场景 |
|---|---|---|---|
| WGS84 经纬度 | 4326 | 度 | 在线底图、常规制图 |
| CGCS2000 经纬度 | 4490 | 度 | 国内成果报批、存档 |
| CGCS2000 Gauss-Kruger 3度带 | 4547等 | 米 | 面积、距离量算与统计 |
| WGS84 Web Mercator | 3857 | 米 | Web地图切片、前端展示 |
如果只做显示,4326和4490差别不大;如果算路网里程、县域面积,就必须先转成以“米”为单位的投影坐标系。很多人在这一步贪方便直接用3857,结果高纬度长度失真,实际里程算出来偏大。放在江苏省这个维度下,3度分带的高斯投影更适合做精确量算。
提示:拿到数据包后如果发现
.cpg文件丢失,属性表中文显示乱码,不要慌,打开QGIS数据源管理器,把编码临时改成GBK或GB18030,一般就能正常显示。千万别用Excel直接开.dbf,Excel对中文编码的识别能力相当有限,很容易把整列属性显示成乱码而不自知。
3. 把数据真正跑起来:体检、拓扑修复与属性预处理
从网盘下载解压的数据,不等于可以直接用于分析。行政区划边界对不齐、面要素重叠、路网在县域边界被截断,是省级基础数据里最常遇到的问题。先花十几分钟做一次“体检”,把明显问题清掉,后面正式跑分析时才不会被各种奇怪的几何错误打断。
3.1 先用QGIS做一次图层“体检”
把省、市、县、公路网和道路网几个图层的数据源连接建立好之后,第一步不是急着配颜色出图,而是检查三层内容:空值情况、几何有效性、坐标范围。
空值检查在属性表里用表达式跑一遍就行。以县级面为例,打开字段计算器,新建文本字段CHECK,表达式写:
if("NAME" is null or "ADCODE" is null, '有问题', '正常')这个表达式的逻辑很直接:判断NAME和ADCODE两个字段是否为空,任何一个为空都会被标记出来。行政区划数据里名称和代码是算法主键,这两列如果大量为空,后面关联统计口径的时候基本没法用。
几何有效性检查用QGIS菜单里的“矢量 → 几何工具 → 检查有效性”。这一步会找出自相交、环重叠之类的问题要素,并把出错位置标出来。跑完看输出面板,如果县级面只有少量记录报错,用后边的Fix geometries处理一次即可;如果报错数量上百,说明某个源数据本身有问题,换一个图层或者找坐标更准的版本重下更省时间。
坐标范围的确认,可以用前文提到的ogrinfo命令,也可以把QGIS画布右下角状态栏的“坐标显示”打开,鼠标移到江苏最东侧的沿海位置看一眼数值。如果是经纬度,显示的是带小数的120度左右;如果是投影坐标,显示是百万级别。判断原则就一句话:面积、周长、路网长度这类量算操作必须在地理坐标系转投影坐标系之后进行,顺序不要搞反。
3.2 拓扑修复:面缝隙、细碎多边形和重叠面
省级数据包里的“省、市、县”三级面要素,往往由不同的数据源拼接而成,最常见的是县域面之间有一条头发丝级别的白色缝隙。肉眼看不太出来,但在空间查询做相交判断时,缝隙会直接导致“某个点在面外”的诡异结果。要根治这个问题,不能只靠肉眼描边,需要用拓扑工具把公共边对齐。
QGIS中比较稳妥的组合是:先用“处理工具箱”里的Fix geometries把几何非法性问题修一遍,再用Delete duplicate geometries把完全重复的要素删掉,最后按指定字段对边界做“节点捕捉”。
在GDAL环境下,一条命令可以一次性完成几何修复:
ogr2ogr -f "ESRI Shapefile" county_fixed.shp county_raw.shp -makevalid -nlt PROMOTE_TO_MULTI-makevalid参数让GDAL调用内部几何修复算法,把自相交断面自动重建为合法面;-nlt PROMOTE_TO_MULTI确保输出统一转成MultiPolygon,避免后续软件兼容性问题。这个命令只处理几何结构,不改动属性表。
如果面要素之间还存在“重叠压盖”而不是单纯的缝隙,就需要用GRASS工具了。在工具箱里搜v.clean,设置“Cleaning tool”为break,再叠加rmdupl步骤。节点捕捉阈值怎么设,取决于数据的坐标单位。经纬度坐标系下阈值填0.0001度(大约10米量级),这个值既不会过度粘连要素,又能消除明显裂缝;投影坐标系下直接按1米或2米填。阈值填太大,会把相邻道路和边界线全部吸到一起,产生大量不自然的直角顶点;填太小则等于没修。
3.3 属性关联:行政区划代码当主键,别用中文名
面图层的属性关联,建议统一使用行政区划代码字段。举个例子,你想统计每个县的公路网里程,最简单的做法是用县级面的ADCODE字段和道路图层的所属县字段做连接。但道路图层并不一定预置了区县代码,这时候需要先做空间连接——把道路线落到县级面里,把面的ADCODE赋到线属性中。
用PyShp写一个逐图层扫描脚本,可以同时检查每一层的关键字段空值情况:
import shapefile # 图层清单,按需替换路径 layers = { "county": "data/admin/county.shp", "city": "data/admin/city.shp", "road": "data/network/road_2022.shp", } key_fields = ["ADCODE", "NAME", "CLASS"] for name, path in layers.items(): sf = shapefile.Reader(path) records = sf.records() total = len(records) missing = {f: 0 for f in key_fields} for rec in records: # rec 是记录对象,字段顺序和读取结果与文件定义一致 for field in key_fields: try: value = rec[field] except Exception: value = None if value is None or str(value).strip() == "": missing[field] += 1 print(f"{name}: 要素总数 {total}, 缺失情况 {missing}")这段脚本通过shapefile.Reader读取每个Shapefile的记录,遍历每条属性记录统计关键字段的空值数量。注意PyShp的rec[field]默认按字段名取值,如果字段名不匹配会抛异常,脚本里用try-except兜住,把缺失标记出来。在真实项目里,我会把layers字典替换成实际解压后的目录结构,先跑一遍再决定哪些图层需要回炉清洗。
防止字段名踩坑的另一个办法是在QGIS里打开属性表看字段别名,有的数据包字段叫XZQHBM,有的叫ADCODE,还有的带GB_CODE前缀,全凭发布者心情。写脚本统计之前先肉眼确认一遍字段名,比自己猜字段拼写靠谱得多。
4. 避坑:这份省级路网数据最容易出的五个问题
基础数据包的坑不在“能不能打开”,而在于打开之后各种隐蔽细节让你在统计阶段翻车。下面五条是我在经手类似项目时反复遇到的,每一条都值得在开工之前先对照自查。
4.1 属性表中文乱码:不是文件坏了,是编码没对上
现象:在QGIS里导入县级面,打开属性表,“南京”变成“鍗椾含”,“苏州市”变成一串问号和特殊符号。
原因:SHP的.dbf属性文件是dBase III 格式,中文编码通常为GBK或GB18030;而QGIS默认按UTF-8解码,两边对不上就出现乱码。
解决:右键图层打开“图层属性→数据源→图层编码”,把编码从UTF-8改成GBK或GB18030,乱码立即消失。如果这个数据包后面还要交换给其他团队,建议先在QGIS里对每个图层“另存为”,在导出选项里强制选择UTF-8编码,做一次批量转码,省得别人拿到手又踩一遍。
4.2 县级面之间出现缝隙或重叠:多源拼接的必然结果
现象:把县级面渲染成相邻不同色块,放大后能看到边界处有白色细缝;或者在属性表里统计面积,把十三个市的面两两相交,重叠面积合计达到几百平方公里。
原因:省、市、县三级数据如果来自不同比例尺或不同年份的测绘成果,公共边界就会有位置偏差。线精度在几十米时,肉眼无法察觉,叠加分析和求交计算会暴露问题。
解决:先确定权威参考层,一般以省级边界为准线,按v.clean的节点捕捉把所有县级边界吸到省级边界上;再验证每个县的面积总和与省级面积差异,差异超过3%就要检查是否有大面积重叠。这一步做完以后再做任何相交查询,结果才稳定。
4.3 道路网在县域边界处“断头”:统计里程对不上
现象:沿某条省道把一整段完整公路连接起来,但在经过地级市边界时,线要素被切成两段,属性也断成两条记录。按全路网汇总里程时,比官方统计公报数值短了上百公里。
原因:原始数据在采集时按行政区分幅存储,图幅边界上的道路被硬性切分,没有做跨边界拼接。
解决:先用名称排序看一下断头规律,再用PostGIS的ST_LineMerge或者GRASSv.clean的break+rmdupl进行线合并。合并后需要按道路名称字段重新计算里程,而不是直接累加原始属性表里的LENGTH字段。执行前先备份,自动合并偶尔会把两条同名但不相交的道路错误地接成一条。
4.4 坐标系误判:面积单位变成“平方度”
现象:在QGIS里对县级面用“字段计算器”算面积,表达式写的是$area,结果数值全是小数点后六位,最大几十,怎么看都不像是平方公里。
原因:几何数据还处于WGS84或CGCS2000经纬度坐标系,面积计算在“度”的单位下进行,产出的是平方度而不是平方米。省域范围跨经度约四到五度,在低纬度区域一“度”的实际距离并不固定,平方度数值完全没法用于统计。
解决:分析前把所有面要素通过ogr2ogr或者QGIS“导出→另存为”重投影到一个高斯投影坐标系,江苏范围常用CGCS2000 / Gauss-Kruger 3-degree zone(按具体分带对应带号)。重投影之后,用$area算出的结果就是平方米,除以1000000即得平方公里。计算道路长度同理,先重投影再使用$length。
4.5 同一要素重复出现:增量更新留下的历史残留
现象:查询某个区县的面要素,结果返回两条完全重叠的几何记录,属性字段一个版本旧一个版本新;或某条高速公路在同一条目录下出现两次,长度属性略有差异。
原因:不同批次的增量更新没有做全量覆盖,而是直接追加到原图层;更新人员把新增路段“堆”在原路网上面,旧路段没有删除。
解决:先用行政区划代码字段做分组去重,SQL逻辑是GROUP BY adcode HAVING count(*)>1,把重复项找出来;再比较要素的入库时间字段(如有)或几何节点数量,决定保留哪一份。这里强调一点,自2022年7月之后的路网项目,我养成了一个习惯:每次做全量导入之前先清空目标表,再执行一次Delete duplicate geometries,不给历史残留留机会。
注意:以上五个问题,有一部分可以靠自动修复解决,比如乱码和坐标系错误;有一部分只能靠“选权威源、建立参考层、人工抽检”才能压住,比如边界缝隙。自动修复能解决80%的几何抖动,剩下20%输出结果仍然可疑时,就回到QGIS里手动对齐一两个节点,成本反而最低。
5. 行政区划+路网联合分析:按县裁剪、里程统计与道路密度计算
把面数据和线数据叠加在一起,最常见也最实用的分析任务是:算每个县境内的公路里程,再除以县域面积得到路网密度。听上去简单,真正跑起来有很多实现层次上的选择。
5.1 先想清楚“先剪再算”还是“先JOIN再算”
拿到县级面和全路网数据,很多新手第一时间会“裁剪数据集”,把每个县套一个按属提取,剪出一条县域道路子集,然后再汇总。这个做法在县城数量少时可行,但江苏省内县区单位数量较多,反复裁剪会产生大量中间冗余文件,而且裁剪后的每段路都会继承原属性但丢失“属于哪个县”的对应关系,后期合并表麻烦。
更稳妥的做法是“空间连接+聚合”。让所有道路线和县级面做相交判断,把道路要素赋予所在县的ADCODE和NAME,再进行分组统计。这样只跑一次几何运算,就不会产生几十个中间Shapefile。
两种路径的取舍取决于数据量。单县手工裁剪适合小范围抽查;全省级别务必走空间连接聚合。
5.2 PostGIS里的里程统计与密度计算
假设已把这份SHP导入PostGIS空间数据库,表结构类似county_2022和road_2022,下面这组SQL可以完成按县统计路网里程:
-- 按县级行政面统计各等级道路里程 -- 先将几何统一转至高斯投影坐标系(以米为单位) SELECT c.adcode, c.name, sum(ST_Length(r.geom_proj)) / 1000 AS road_km FROM county_2022 c JOIN road_2022 r ON ST_Intersects(c.geom, r.geom) GROUP BY c.adcode, c.name ORDER BY road_km DESC;这段SQL要先解释一个关键陷阱:JOIN条件是两图层的原始几何相交,输出汇总的是“被包含或穿越该县的所有道路”的原始长度。如果一条跨县道路同时穿越三个县,这条路的整体长度会被计入三个县,里程汇总重复。要得到“各县境内实际分布里程”,必须改用道路被县域切割后的部分来计算:
-- 避免跨县道路重复计入:用交集后的长度 SELECT c.adcode, c.name, sum(ST_Length(ST_Intersection(ST_Transform(r.geom, 4547), ST_Transform(c.geom, 4547)))) / 1000 AS road_km FROM county_2022 c JOIN road_2022 r ON ST_Intersects(c.geom, ST_Transform(r.geom, 4547)) GROUP BY c.adcode, c.name;这里逻辑就顺了:ST_Intersection先裁出道路落在该县面内的那一段,再量出长度,每个县的公里数才算准确。ST_Transform(geom, 4547)的作用是把几何从原始坐标系转换到以米为单位的高斯投影,避免ST_Length返回经纬度下的“度”单位。第4547号EPSG是适用于江苏等地的CGCS2000三度带投影码,具体使用哪个带号要根据数据实际经度所在分带确定。
公路网密度就是里程除以县级面面积:
CREATE VIEW density_2022 AS SELECT c.adcode, c.name, sum(ST_Length(ST_Intersection(ST_Transform(r.geom, 4547), ST_Transform(c.geom, 4547)))) / 1000 / (ST_Area(ST_Transform(c.geom, 4547)) / 1e6) AS density_km_per_km2 FROM county_2022 c JOIN road_2022 r ON ST_Intersects(c.geom, ST_Transform(r.geom, 4547)) GROUP BY c.adcode, c.name;面积除以1e6是把平方米换算成平方公里,得到的密度单位是“公里/平方公里”,也就是每平方公里土地上铺了多少公里路。这个数值在不同县域之间对比,能很直观地反映路网整体水平。
5.3 导出结果:GeoJSON出图,Shapefile归档
统计结果落在数据库里以后,还需要导出成便于制图和分发的格式。用GDAL指令导出单个图层十分轻量:
ogr2ogr -f GeoJSON density_result.geojson \ PG:"host=localhost user=gisuser dbname=jiangsu password=***" \ -sql "SELECT * FROM density_2022 ORDER BY density_km_per_km2 DESC" \ -t_srs EPSG:4326PG:数据源直接把PostgreSQL表读出来,-sql指定查询语句,-t_srs EPSG:4326把结果转成经纬度坐标系,方便前端展示。导出到Shp做存档也可以把输出格式换成“ESRI Shapefile”。需要说明的是,-sql里如果再写复杂聚合,建议先在库里建好视图,导出时直接引用视图名称,省得命令行SQL写太长难维护。
到这一步,所有和联合分析相关的“结果数据”就都能复现了。如果这份包里没有铁路网图层,而你要做的是公铁对比分析,需要另外找铁路专业图层,不要拿公路网来顶替。路网统计结果和官方公报对不上时,优先检查的是字段TYPE选没选对、有没有重复统计。
6. 一个更重要的技巧:把这套数据做成团队可用的基线版本
数据清洗、拓扑修复、合并统计都做完之后,这个包就不再是“下载来的原始文件”,而是一个被验证过的成果数据集。下一件该做的事情,就是防止它退化成又一个版本混乱的生产事故。
6.1 用GeoPackage代替散装Shapefile做存档
大家习惯于保留SHP原样是因为它兼容性强,但项目内部共享和版本管理场景下,我更建议统一转成GeoPackage单文件格式存档。一个.gpkg文件天然支持多图层、多属性索引和空间索引,在QGIS里加载速度也比SHP快。
# 建一个归档目录,所有图层统一输出到同一个gpkg内 mkdir -p baseline ogr2ogr -f GPKG baseline/jiangsu_baseline.gpkg data/road_2022.shp -nln road_2022 ogr2ogr -f GPKG baseline/jiangsu_baseline.gpkg data/county_2022.shp -nln county_2022-nln参数指定图层在GeoPackage里的名字。两行命令会把省、市、县、路网多张表都汇入同一个库文件。之后团队内部传递只需要拷一个文件,再也不用担心漏带.shx或.dbf。
6.2 计算哈希值,给数据包一个“指纹”
SHP是二进制格式,Git对这类非文本文件的diff能力基本等于零,靠文件名区分版本很容易把新旧弄混。我习惯在每个基线目录里放一份校验文件:
sha256sum county_2022.shp county_2022.dbf road_2022.shp road_2022.dbf > checksum.txtsha256sum会为每个文件生成一段哈希指纹,任何一个字节发生变化,指纹都会改变。版本更新之后重新跑一遍,两个checksum.txt一对比就能确认数据是否被预料之外地改动过。这个动作虽然简单,但能省掉大量“这个文件到底是最新的还是旧的”的质疑。
有一回某位同事在三个星期后告诉我“某个县的面要素可能改过”,我把旧版备份的哈希值和当前文件一比对,发现确实整个县边界面积对不上,最后查出来是某次局部更新时把整层覆盖了,但覆盖完没有同步更新入库记录。从那以后,我每次更新全省路网数据都强制按“导入 → 拓扑修复 → 计算哈希 → 更新归档”走,任何一步没走完都不能算完成。希望这个习惯也能帮到你少踩一次版本坑。
本文还有配套的精品资源,点击获取