资讯详情

江苏shp数据实战:图层拆解、坐标系统一与叠加分析

📅 2026/10/9 21:23:29 | 华诺云谱 👁 阅读
江苏shp数据实战:图层拆解、坐标系统一与叠加分析
简介这份资源面向GIS初学者、地理信息研究者及规划从业者提供江苏省河流、湖泊、县域、高速公路等核心地理要素的Shapefile矢量数据可直接导入ArcGIS、QGIS等软件进行地图制作与空间分析。压缩包共83个文件约974KB以shp、dbf、shx、prj、sbn、sbx等标准GIS文件类型为主分别承载几何图形、属性表、空间索引与投影信息另含少量xml与atx辅助文件结构规范、开箱即用。目前已有2381人学习下载适合需要快速获取江苏基础地理底图、开展水文与交通专题分析的用户。借助这些数据可叠加河流与湖泊评估洪水风险结合县域边界分析区域发展差异或利用高速公路网络优化交通布局为科研、规划与决策提供可靠的空间数据支撑。1. 江苏 shp 数据到手后先别急着拖进 GIS搞清楚这四类图层再动手做江苏相关项目的同行大概率都遇到过这种场景领导或甲方丢过来一句“把江苏的河流、湖泊、县域、高速都叠到一张图上”然后你打开文件夹发现是一堆后缀为.shp的文件双击打不开拖进 QGIS 能显示但属性表里字段名全是拼音缩写坐标系也不统一。这份“江苏省河流、湖泊、县域、高速等 shp 数据”就是解决这类问题的起点——它把省级行政边界、水系面、线状河流、高速公路路网这几类常用底图打包在一起省去了你从多个公开渠道分别抓取、清洗、对齐坐标系的重复劳动。它适合谁做国土空间规划、交通流量分析、水文连通性评估、县域经济统计制图的学生和一线工程师以及需要快速搭建江苏底图原型的开发者。不适合谁需要亚米级精度做工程放样的测绘人员——这类公开整理数据通常到不了那个级别。下面按“先认清图层 → 再统一坐标系 → 再叠加分析 → 再避坑 → 再进阶”的顺序拆一遍每一步都落到可复现的命令和参数上。2. 图层拆解与坐标系判定用 ogrinfo 把黑匣子打开拿到 shp 包第一件事不是打开 ArcGIS 点“添加数据”而是用命令行工具把每个文件的元数据读出来。GDAL 套件里的ogrinfo是最稳的入口它不依赖图形界面输出的是纯文本方便你直接判断坐标系、字段类型和几何类型。2.1 用 ogrinfo 批量读取图层摘要假设你把数据解压到了jiangsu_shp/目录下先列出所有.shp文件再逐个读取摘要信息# 进入数据目录 cd jiangsu_shp/ # 列出所有 shp 文件确认文件数量与命名规律 ls -1 *.shp # 对单个文件读取摘要-so 表示只输出摘要不输出要素 ogrinfo -so -al jiangsu_river.shp # 批量读取所有 shp 的坐标系与几何类型输出到文本方便比对 for f in *.shp; do echo $f ogrinfo -so -al $f | grep -E Geometry|Feature Count|Extent|PROJCRS|GEOGCRS|ID\[ done逻辑说明-so是 summary only避免几万个要素全部打印拖慢终端-al表示所有图层shp 通常只有一个图层。grep过滤出几何类型、要素数量、范围和坐标系名称这几项决定了后续能不能直接叠加。参数说明Geometry告诉你这是点、线还是面——河流可能是线Line String湖泊是面Polygon县域是面高速是线。Extent是经纬度或投影坐标下的包围盒如果两个图层的 Extent 数量级差很远比如一个在 118~121另一个在 300000~500000说明坐标系类型不同不能直接叠。PROJCRS或GEOGCRS是坐标系定义前者是投影坐标系后者是地理坐标系。2.2 坐标系不一致时的两种处理策略常见情况是县域边界用的是EPSG:4326WGS84 经纬度而高速路网用的是EPSG:3857Web 墨卡托或者某个地方投影带如EPSG:4547CGCS2000 3 度带。直接叠加会出现“图层跑到南极”或“偏移几百米”的翻车现场。处理策略一统一到地理坐标系做展示。适合只做可视化、不做面积量算的场景。# 把高速路网从投影坐标系转回 WGS84 经纬度 ogr2ogr -f ESRI Shapefile \ -t_srs EPSG:4326 \ highway_wgs84.shp \ jiangsu_highway.shp # 验证转换后的坐标系 ogrinfo -so -al highway_wgs84.shp | grep GEOGCRS处理策略二统一到投影坐标系做量算。适合要计算河流长度、湖泊面积、县域面积的场景。江苏常用的投影是 CGCS2000 3 度带中央经线 120°E 对应EPSG:4547。# 把县域边界从经纬度转到 CGCS2000 3 度带 ogr2ogr -f ESRI Shapefile \ -t_srs EPSG:4547 \ county_projected.shp \ jiangsu_county.shp # 检查转换后 Extent 是否落在合理范围单位米 ogrinfo -so -al county_projected.shp | grep Extent逻辑说明ogr2ogr的-t_srs指定目标坐标系源坐标系 GDAL 会自动从.prj文件读取。如果.prj缺失或写错需要加-s_srs手动指定源坐标系否则转换结果会偏到离谱。参数说明-f ESRI Shapefile指定输出格式shp 对字段名长度有限制最多 10 个字符如果原数据字段名很长转换后可能被截断建议先检查字段名。EPSG:4547是 CGCS2000 / 3-degree Gauss-Kruger CM 120E江苏全境基本落在中央经线 120°E 附近变形可控。提示如果.prj文件缺失不要凭感觉猜坐标系。先用ogrinfo看 Extent 数值范围经纬度范围在 -180~180 之间投影坐标通常是几十万到几百万。再结合数据来源判断实在不确定就找提供方确认。3. 用 Python 做叠加分析与属性筛选geopandas 实操命令行能看清数据结构但真正做叠加、筛选、统计还是得上 Python。geopandas是目前最顺手的库底层调 GDAL 和 Fiona读写 shp 和读 CSV 一样自然。3.1 环境准备与依赖安装不要用pip install geopandas直接装容易因为 GDAL 版本不匹配报错。推荐用 conda 建独立环境# 创建独立环境指定 python 版本 conda create -n js_shp python3.10 -y conda activate js_shp # 通过 conda-forge 安装GDAL 依赖会自动解决 conda install -c conda-forge geopandas matplotlib shapely fiona pyproj -y # 验证安装 python -c import geopandas as gpd; print(gpd.__version__)逻辑说明geopandas依赖fiona读写文件、shapely做几何运算、pyproj做坐标转换。conda-forge 渠道的包经过编译测试比 pip 轮子更稳。参数说明Python 3.10 是当前兼容性较好的版本3.11 以上部分地理库轮子还没跟上。如果公司内网只能用 pip先装GDAL的 whl 包再装fiona最后装geopandas顺序不能乱。3.2 读取多图层并做空间叠加下面这段代码读取河流、湖泊、县域、高速四个图层统一坐标系后统计每个县内的河流长度和高速里程。import geopandas as gpd import pandas as pd # 读取四个图层指定编码避免中文乱码 river gpd.read_file(jiangsu_river.shp, encodingutf-8) lake gpd.read_file(jiangsu_lake.shp, encodingutf-8) county gpd.read_file(jiangsu_county.shp, encodingutf-8) highway gpd.read_file(jiangsu_highway.shp, encodingutf-8) # 统一到 CGCS2000 3 度带方便量算长度和面积 target_crs EPSG:4547 river river.to_crs(target_crs) lake lake.to_crs(target_crs) county county.to_crs(target_crs) highway highway.to_crs(target_crs) # 检查几何有效性修复自相交等常见问题 for name, gdf in [(river, river), (lake, lake), (county, county), (highway, highway)]: invalid gdf[~gdf.geometry.is_valid] if len(invalid) 0: print(f{name} 存在 {len(invalid)} 个无效几何尝试修复) gdf[geometry] gdf.geometry.buffer(0) # 用县域边界裁剪河流得到每个县内的河流片段 river_clip gpd.overlay(river, county, howintersection) # 按县名分组计算河流总长度单位米转成公里 river_clip[length_km] river_clip.geometry.length / 1000 river_stats river_clip.groupby(county_name)[length_km].sum().reset_index() # 同样方式统计高速里程 highway_clip gpd.overlay(highway, county, howintersection) highway_clip[length_km] highway_clip.geometry.length / 1000 highway_stats highway_clip.groupby(county_name)[length_km].sum().reset_index() # 合并两个统计结果 result pd.merge(river_stats, highway_stats, oncounty_name, howouter) result.columns [县域名称, 河流长度_km, 高速里程_km] result.to_csv(jiangsu_county_stats.csv, indexFalse, encodingutf-8-sig) print(result.head(10))逻辑说明to_crs做坐标转换buffer(0)是修复无效几何的常用技巧——它把自相交的多边形重新构造成有效几何代价是可能轻微改变边界但比直接报错强。gpd.overlay做空间相交howintersection表示只保留两个图层重叠的部分。groupby按县名汇总geometry.length在投影坐标系下返回米除以 1000 得到公里。参数说明encodingutf-8针对属性表里有中文的情况如果原数据是 GBK 编码改成gbk。county_name是假设的字段名实际字段名要用county.columns先看一眼常见的有NAME、县名、XZQMC等。howouter保证即使某个县没有河流或高速也会出现在结果里不会丢县。3.3 属性筛选与导出不是所有分析都需要全量数据。比如只关心长江干流沿线的县或者只关心高速公路缓冲区 5 公里内的湖泊。# 筛选名称包含“长江”的河流 changjiang river[river[river_name].str.contains(长江, naFalse)] # 对高速做 5 公里缓冲区再与湖泊求交 highway_buffer highway.copy() highway_buffer[geometry] highway.geometry.buffer(5000) # 单位米 lake_near_highway gpd.overlay(lake, highway_buffer, howintersection) # 导出为 GeoJSON方便在网页端加载 lake_near_highway.to_file(lake_near_highway.geojson, driverGeoJSON)逻辑说明str.contains做模糊匹配naFalse避免空值报错。buffer(5000)在投影坐标系下单位是米如果数据还在经纬度坐标系5000 就是 5000 度结果会离谱——这也是为什么前面强调先转投影。to_file导出 GeoJSON 时GDAL 会自动把坐标系写成 WGS84 经纬度因为 GeoJSON 规范要求用经纬度。参数说明driverGeoJSON指定输出格式如果导出 shp 就用driverESRI Shapefile。GeoJSON 文件通常比 shp 大但单文件、跨平台、网页端直接读适合做轻量交付。4. 避坑与排查这五类问题我几乎每次都能遇到4.1 现象图层叠加后位置偏移几百米原因两个图层坐标系定义不一致或者.prj文件写错了但 GDAL 没报错。常见于从不同渠道拼凑的数据一个用 WGS84一个用 CGCS2000两者在江苏范围内差几十米到上百米。解决用ogrinfo -so -al逐个检查GEOGCRS或PROJCRS确认 EPSG 代码。如果.prj缺失用-s_srs手动指定源坐标系再转换。不要凭肉眼判断“看起来差不多”。4.2 现象属性表中文显示为乱码原因shp 的.dbf文件编码不统一有的用 GBK有的用 UTF-8GDAL 默认按 UTF-8 读遇到 GBK 就乱。解决读取时显式指定encodinggbk或encodingutf-8试两次就知道。如果字段名本身乱码用gdf.columns看实际读出来的名字必要时用rename重命名。4.3 现象gpd.overlay报拓扑错误或结果为空原因几何自相交、多边形不闭合、或者两个图层坐标系虽然 EPSG 相同但基准面不同如北京54 和 CGCS2000 都可能是 3 度带但椭球参数不同。解决先跑gdf.geometry.is_valid检查无效的用buffer(0)修复。如果修复后仍为空检查两个图层的 Extent 是否真的重叠——有时候数据范围根本不在一个区域叠加自然为空。4.4 现象shp 字段名被截断成 10 个字符原因ESRI Shapefile 格式本身限制字段名最长 10 个字符GDAL 写入时会自动截断且不报错。解决如果字段名很重要导出时改用 GeoPackage.gpkg或 GeoJSON这两种格式没有字段名长度限制。命令是gdf.to_file(output.gpkg, driverGPKG)。4.5 现象计算长度为 0 或面积异常大原因在经纬度坐标系下直接调geometry.length返回的是度数不是米或者投影带选错了中央经线偏离江苏太远变形放大。解决量算前必须to_crs到投影坐标系。江苏用EPSG:4547或EPSG:4526CGCS2000 / 3-degree Gauss-Kruger CM 117E都可以具体看数据落在哪个带。面积量算同理投影坐标系下geometry.area返回平方米。5. 进阶技巧用 GeoPandas 做批量裁剪与自动化出图数据拿到手最终多半要落到一张图上。如果只是手动在 QGIS 里调样式下次换个省的数据又得重来。更稳的做法是写一个可复用的脚本把裁剪、统计、出图串起来。5.1 按县域批量裁剪并导出独立 shp假设甲方要每个县一个独立的河流 shp 文件手动裁 13 个市、95 个县会疯。用循环批量处理import os import geopandas as gpd # 读取数据并统一坐标系 county gpd.read_file(jiangsu_county.shp, encodingutf-8).to_crs(EPSG:4547) river gpd.read_file(jiangsu_river.shp, encodingutf-8).to_crs(EPSG:4547) # 创建输出目录 os.makedirs(output_by_county, exist_okTrue) # 按县名分组逐个裁剪并导出 for name, group in county.groupby(county_name): # 用当前县的几何去裁剪河流 clipped gpd.clip(river, group) if len(clipped) 0: print(f{name} 无河流数据跳过) continue # 文件名去掉特殊字符避免路径报错 safe_name str(name).replace(/, _).replace(\\, _) clipped.to_file(foutput_by_county/{safe_name}_river.shp, encodingutf-8) print(f{name} 导出 {len(clipped)} 条河流)逻辑说明gpd.clip比overlay更轻量专门做裁剪不保留被裁掉的部分。groupby按县名分组每组是一个只包含当前县的小 GeoDataFrame。safe_name处理县名里可能出现的斜杠等特殊字符避免写文件时报错。参数说明os.makedirs(..., exist_okTrue)保证目录已存在时不报错。encodingutf-8写入时指定编码防止中文属性乱码。如果县名分组字段不是county_name换成实际字段名。5.2 用 matplotlib 快速出图并标注出图不需要 QGISmatplotlib 加 geopandas 就能画。关键是设置中文字体和图例。import matplotlib.pyplot as plt import matplotlib # 设置中文字体Windows 用 SimHeiLinux 用 WenQuanYi matplotlib.rcParams[font.sans-serif] [SimHei, WenQuanYi Micro Hei] matplotlib.rcParams[axes.unicode_minus] False fig, ax plt.subplots(figsize(12, 10)) # 按图层顺序绘制先画面状再画线状 county.plot(axax, facecolornone, edgecolorgray, linewidth0.5) lake.plot(axax, facecolorlightblue, edgecolorblue, linewidth0.3) river.plot(axax, colorblue, linewidth0.5) highway.plot(axax, colorred, linewidth0.8) # 添加标题和图例 ax.set_title(江苏省河流、湖泊、县域与高速分布, fontsize16) ax.set_axis_off() # 保存为高分辨率 PNG plt.savefig(jiangsu_overview.png, dpi300, bbox_inchestight) plt.close() print(出图完成jiangsu_overview.png)逻辑说明facecolornone让县域只显示边界不填充避免盖住下面的湖泊和河流。dpi300保证打印精度bbox_inchestight去掉多余白边。图层顺序很重要——先画的面会被后画的面盖住所以县域边界放最前高速放最后。参数说明font.sans-serif列表里放多个字体名matplotlib 会按顺序找找不到就换下一个。Linux 服务器上通常没有 SimHei用WenQuanYi Micro Hei或Noto Sans CJK SC。如果图例需要显示中文还要在plot里加label参数再调ax.legend()。5.3 一个我踩过的坑别在循环里反复读文件早期写批量裁剪脚本时我在for循环里每次都gpd.read_file读一遍河流数据95 个县读了 95 次跑了十几分钟。后来改成循环外读一次循环内只做clip时间降到几十秒。血泪经验I/O 是瓶颈能复用就复用。从那以后我每次写批量处理脚本都强制走一遍“读一次、循环内只算不读”的检查。希望帮到你。本文还有配套的精品资源点击获取
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑