简介:江苏省Shapefile矢量地图数据,涵盖行政边界、河流、道路、城市、乡镇等基础地理要素,适用于GIS学习者、规划人员及科研工作者,可用于地图制图、空间叠加、缓冲区分析及统计建模等场景,帮助读者快速建立江苏省空间分析底图。压缩包共9个文件,主要包含.shp几何数据、.dbf属性表、.shx索引、.prj坐标参考及配套辅助文件,导入ArcGIS、QGIS等软件时需保持各文件同目录,以保证数据完整可读。包体仅293KB,体积轻量,便于下载与共享。目前已有692人学习浏览,适合需要区域基础底图开展数据预处理、地理加权回归或趋势预测的读者。数据保留矢量拓扑结构,可灵活关联人口、经济等外部统计字段,揭示地域特征并支撑城市规划、交通规划与灾害风险评估等实践决策。
1. 江苏shp地图到底解决了什么问题:拿到数据后先确认这三件事
同样一份江苏shp地图,有人加载一下就能叠底图出图,有人折腾半天还是边界漂移、属性表乱码、统计图对不上。区别不在数据本身,而在你动它之前有没有先把坐标系、图层组织和属性字段这三件事想清楚。本文的目标,是让你拿到一份江苏shp地图后,从文件结构到可视化都能独立走通,而不是被网上那些版本不一、坐标混乱的包折腾到怀疑人生。适合做数据可视化、Web地图展示、行政区划统计分析的前端和数据分析同学,也适合手里已经有一份shp但一直没跑明白的GIS从业者。先别急着双击打开,跟着下面的思路走一遍。
2. 给江苏shp地图做体检:文件结构、属性表和坐标系怎么一次看清
2.1 用geopandas读一遍:先看crs、geometry和字段
shp不是单文件,一个完整的shapefile至少需要三个文件:.shp存几何、.dbf存属性、.shx存索引。很多新手只拿到其中一个就解压,结果读出来的内容缺胳膊少腿。我应该先做一遍体检,用geopandas读取后,一次性确认几何类型、坐标系和字段清单。
import geopandas as gpd # 读取江苏shp,如果属性表是中文,优先尝试gbk编码 gdf = gpd.read_file("jiangsu.shp", encoding="utf-8") # 体检第一步:看几何类型与坐标系 print("geometry类型:", gdf.geom_type.unique()) print("当前坐标系:", gdf.crs) # 体检第二步:看字段与数据类型 print("字段列表:", gdf.columns.tolist()) print("前5行属性:", gdf[["name", "adcode"]].head())这段代码里,geom_type会告诉你图层是面还是线还是点,crs会显示投影坐标系代码,比如EPSG:4490就代表CGCS2000地理坐标系。字段里name是地市或区县名称,adcode是行政区划代码,这是后面做统计合并的关键。如果读取时中文变成问号或乱码,把encoding换成"gbk"再读一次。
2.2 面线点三层数据:分别负责什么,取数据时怎么选
江苏shp地图通常会包含多个图层文件,最常见的有省界、地市界、区县界,还有道路、水系、居民地等。面图层用来做行政区域统计和分级填色,线图层用来画道路和河流,点图层用来标注城镇和地标。我一般习惯把面图层当成底图骨架,线图层做辅助参考,点图层只在对标注有明确需求时才用,否则会稀释整体信息密度。判断一个面图层是否足够精细,最直接的办法是看它的边界节点数和面积计算是否接近官方口径。
用面图层做统计时,记得确认两个问题:这个面是地市一级还是区县一级,以及内部是否包含飞地。飞地在shp里往往表现为一个面要素被另一个面要素包围,统计时会重复计数,需要特殊处理。
2.3 属性表里值得提前确认的字段
属性表是shp地图里最容易踩坑的地方。江苏相关数据的属性表里,我每次都会确认这几个字段:一是行政区划代码,用来和统计年鉴数据做join;二是名称字段,要注意繁体、旧名或带后缀的写法;三是面积字段和人口字段,先确认单位是平方公里还是亩,避免后面画图时数值差一个量级。
# 检查属性表是否有空值或重复代码 print("空值统计:") print(gdf.isnull().sum()) print("行政区划代码重复数:", gdf["adcode"].duplicated().sum())如果adcode存在重复,合并外部统计表时会出现一对多,导致图表数值翻倍。我一般会先把adcode转成字符串,再补零到六级,保证和统计年鉴里的代码位数一致。这一步虽然基础,但能省掉后面一大堆对账的麻烦。
3. 把江苏shp地图转成能用的坐标系:从 CGCS2000/西安80 到 WGS84
3.1 坐标系转换命令手记:geopandas一行搞定
江苏shp地图的坐标系,根据数据来源不同,常见的三种:CGCS2000地理坐标系EPSG:4490、西安80坐标系EPSG:4610、WGS84地理坐标系EPSG:4326。如果你要在高德或Leaflet上叠加底图,底图默认都是EPSG:3857或者EPSG:4326,所以拿到数据后第一件事往往是转坐标系。
import geopandas as gpd gdf = gpd.read_file("jiangsu.shp", encoding="gbk") # 先确认原始坐标系 print("原始坐标系:", gdf.crs) # 转到WGS84地理坐标系,方便Web底图叠加 gdf_wgs84 = gdf.to_crs(epsg=4326) # 查看转换后边界范围 print("转换后边界范围:", gdf_wgs84.total_bounds) # 另存为GeoJSON,Web前端直接用 gdf_wgs84.to_file("jiangsu_wgs84.geojson", driver="GeoJSON")to_crs是投影转换,不是简单重写坐标系标签。这段代码里必须保证gdf.crs不是None,否则转换结果完全错误。如果crs为None,只能先用如下方式手动指定,再转换。
from pyproj import CRS # 依据来源说明设置坐标系,常见是CGCS2000 gdf = gdf.set_crs(CRS.from_epsg(4490)) gdf_wgs84 = gdf.to_crs(epsg=4326)手动指定坐标系是高风险操作,如果原始数据其实是西安80而你标成了CGCS2000,转换后的数据会整体偏移几百米。判断原始坐标系,优先看数据包里的说明文件或坐标系定义文件(.prj),如果都没写,就对比边界和已知参考点位置来确定。
3.2 底图对齐后的“后悔药”:简化、导出GeoJSON
底图叠加后发现边界和卫星图对不上,先确认两点:一是不是坐标系转错了,二是不是边界精度过高导致前端渲染卡顿。江苏省级边界的shp文件通常有几千到几万个节点,直接丢进Web前端加载会明显掉帧,我的做法是简化边界再导出。
import geopandas as gpd gdf = gpd.read_file("jiangsu_wgs84.geojson") # 保留足够精度的简化,容差0.005约合500米 gdf_simplified = gdf.copy() gdf_simplified["geometry"] = gdf.geometry.simplify(tolerance=0.005) # 简化后修复无效几何 gdf_simplified["geometry"] = gdf_simplified.geometry.buffer(0) # 导出轻量级GeoJSON gdf_simplified.to_file("jiangsu_simplify.geojson", driver="GeoJSON") print("简化后节点数量:", sum(gdf.geometry.length for gdf in [gdf_simplified]))simplify使用的是道格拉斯-普克算法,tolerance越大边界越粗糙。0.005适合市级图预览,精确到区县边界才需要调整到0.001。buffer(0)是修复因简化产生的几何裂缝,这个操作在后续计算面积和拓扑查询时很有用。
3.3 常见误用:在没确认坐标系前强行叠图
坐标系转换最大的坑,不是不会写命令,而是根本不知道自己手里的数据是什么坐标系。我翻过车,拿着一份标着WGS84的江苏区县边界,直接叠高德底图,结果整体偏移了200多米,一开始还以为是底图的问题,后来才发现那份数据的真实坐标系是CGCS2000,被发布者错误标注了。
事前校验的方法很简单,找一个已知地标的经纬度,和shp里对应点的坐标对比,差距在0.001度以内基本没问题,差距明显就说明坐标系或数据本身有问题。不要盲目相信文件名里的“WGS84”,也不要只依赖crs字段,配合边界范围一起看才能确定。
4. 区县地市分层可视化:用江苏shp地图画一张分级统计图
4.1 准备统计字段:合并外部表格的正确姿势
拿到江苏shp地图后,大部分使用场景不是看边界,而是把业务数据和地理边界融合在一起,比如各市GDP、人口或房价分布。这一步的关键是join的键要统一格式。行政区划代码在shp里可能是数字,在Excel里可能是文本,直接merge会出大量空值。
import pandas as pd import geopandas as gpd # 读取江苏shp gdf = gpd.read_file("jiangsu_shp", encoding="gbk") # 读取业务统计表 df = pd.read_excel("jiangsu_stats.xlsx") # 统一adcode字段格式:转字符串并补足6位 gdf["adcode"] = gdf["adcode"].astype(str).str.zfill(6) df["adcode"] = df["adcode"].astype(str).str.zfill(6) # 左连接,保留所有地理边界 merged = gdf.merge(df, on="adcode", how="left") print("合并后为空值的统计字段:", merged["value"].isnull().sum())left join保证了地图完整性,某个地区没有业务数据时,这个区域的统计字段就是空值,画图时空值区域会被跳过或填充为灰色,方便直接暴露数据缺口。这里最容易被忽略的是adcode的位数,统计年鉴里的市级代码一般是四位,区县级是六位,合并前统一按区域级别补零。
4.2 画地市分级图时怎么控制图例和边界线
matplotlib配合geopandas可以快速出图。画江苏省地市级分布时,我只画地市边界,区县边界作为内部细线可以叠加,但要注意线宽和颜色让层级分明。图例的区间数量建议控制在5档左右,过多会让读者失去比较。
import matplotlib.pyplot as plt fig, ax = plt.subplots(1, 1, figsize=(8, 10)) # 绘制面,按业务字段分级着色 merged.plot(column="value", ax=ax, cmap="OrRd", legend=True, edgecolor="black", linewidth=0.5, missing_kwds={"color": "lightgrey", "label": "暂无数据"}) # 叠加地市级边界线,加粗显示 gdf_boundary = merged.dissolve(by="city_code") gdf_boundary.boundary.plot(ax=ax, color="black", linewidth=1.2) plt.axis("off") plt.show()dissolve按地市代码合并内部区县边界,生成地市轮廓线,加粗后让读者一眼看出层级结构。edgecolor和linewidth控制面边界,如果觉得县界太密,就只保留地市边界,把区县边界线宽设为0.2或直接省略。
4.3 飞地与嵌套关系:画区县图时常用的双层图层组织
江苏部分区县的行政区划存在飞地,也就是一块区域归A管辖,但地理位置在B辖区内部。shp里飞地通常作为一个独立要素存在,如果只是简单按区县名字分组,画出来会出现叠加覆盖。处理飞地我一般会先用区域代码分组,再检查每个分组内的几何数量。
# 统计每个区县的几何要素数量,排查多几何 geo_count = gdf.groupby("adcode").size().reset_index(name="count") print("存在多个几何要素的区县:", geo_count[geo_count["count"] > 1])如果某个区县有多个要素,那要么是飞地,要么是岛屿,要么是数据分割问题。可视化时建议将它们合并成一个MultiPolygon再映射填色,避免颜色被覆盖或统计面积偏大。合并操作使用dissolve即可,会按指定字段把同一个区县的所有几何合并。
5. 江苏shp地图避坑:属性乱码、边界漂移与区划过时怎么排查
5.1 属性表乱码
现象:读取shp后,name字段显示为“???”或一堆乱码。原因:属性表.dbf文件是老版本编码,常见为GBK或GB2312,但读取时用了utf-8。解决:读取时指定encoding="gbk",如果依旧乱码,逐个尝试"gb2312"和"utf-8-sig",最稳妥的是用文本编辑器打开shp同目录下的.cpg文件查看声明编码。我有一次遇到一个混合编码的属性表,字段名是utf-8但字段值是gbk,只能先分字段重新编码转成统一格式再入库。
gdf = gpd.read_file("jiangsu.shp", encoding="gbk") # 确认名称字段是否正常 print(gdf["name"].head())5.2 边界漂移
现象:shp边界叠加卫星底图或在线地图后,整体错位100米到几百米。原因:原始数据坐标系和底图坐标系不一致,且未确认坐标系就盲目转换。解决:先确认坐标系声明,再用坐标转换工具校验。如果原始数据是西安80,要使用EPSG:4610加towgs84参数转换,单纯从epsg:4610转到4326精度有限,部分来源的数据需要七参数才能和WGS84精确对齐。实用做法是找一个已知的精确点位,对比转换前后的经纬度偏差,偏差超过0.002度就考虑换数据源。
5.3 边界不闭合或自相交
现象:计算江苏某个区县的面积时数值异常,或者dissolve后出现镂空。原因:原始边界线没有正确闭合,或者简化边界后产生了自相交。解决:使用buffer(0)修复无效几何,检查修复前后有效面积的变化。我在处理一个县级边界时发现原本标注的700平方公里,修复后变成了650,说明原数据本身就存在拓扑错误。
gdf_clean = gdf.copy() gdf_clean["geometry"] = gdf.geometry.buffer(0) invalid_check = ~gdf_clean.is_valid print("无效几何数量:", invalid_check.sum())5.4 行政区划代码过时
现象:属性表里的区县名称和当前官方行政区划对不上,统计数据合并时出现N/A。原因:shp数据发布较早,近年来部分区县经历过撤并或调整。解决:获取最新行政区划代码表,先按旧代码匹配,未命中的再用名称字段模糊匹配。不要直接用名称做join,名称常有简称、旧称差异,一换代码就变成两张表对不齐。
5.5 面积单位不一致
现象:同一张图里,部分区域面积比实际大十倍或小十倍。原因:属性表里面积字段来源复杂,有平方公里、亩、公顷混用的,还有包含飞地面积导致重复计算。解决:不信任属性表里的面积字段,用几何重新计算面积,并统一单位。
# 投影到等积投影后再计算,避免经纬度直接算面积 gdf_area = gdf.to_crs({"proj": "aea", "lat_1": 30, "lat_2": 40, "lat_0": 32, "lon_0": 120}) gdf["area_km2"] = gdf_area.geometry.area / 1_000_000 print("面积前5:", gdf[["name", "area_km2"]].head())AEA等积投影适合江苏这种中纬度区域,计算出的面积最接近真实值,直接经纬度坐标算面积会随纬度变化而扭曲。记住这个习惯,官方统计口径里面积单位是平方公里,对比线上面积时心里就有底了。
6. 进阶:把江苏shp地图做成一个可交互的本地分级统计页
静态图永远是给别人看的,真正自己做数据探索时需要缩放查看局部边界。我习惯把江苏shp地图和业务数据做成一个可交互的本地页面,这样从县级视角切到区县边界,还能按业务字段动态填色。下面用folium做一个最小可用版本,输出为一个独立HTML文件,不需要起服务,双击就能打开。
import json import pandas as pd import geopandas as gpd import folium # 读取已经转好坐标系的江苏shp gdf = gpd.read_file("jiangsu_wgs84.geojson") gdf_json = json.loads(gdf.to_json()) # 演示统计字段 df = pd.DataFrame({ "adcode": gdf["adcode"], "value": range(len(gdf)) }) gdf_merged = gdf.merge(df, on="adcode") # 创建地图,中心点放在江苏中部 m = folium.Map(location=[33.0, 119.5], zoom_start=7) folium.Choropleth( geo_data=gdf_json, data=gdf_merged, columns=["adcode", "value"], key_on="feature.properties.adcode", fill_color="YlOrRd", fill_opacity=0.7, line_opacity=0.5, legend_name="业务指标" ).add_to(m) m.save("jiangsu_interactive.html")这段代码里有两个关键参数需要你根据实际字段调整:key_on的值要匹配GeoJSON属性字段名,columns的第一列必须是shp和业务表共用的键。folium的Choropleth颜色区间默认按分位数分桶,适合查看相对分布。如果你需要支持点击弹窗显示区县名称和指标值,可以在GeoJSON上叠加一层GeoJsonTooltip,把我这个基础版本继续扩展。
做完这个交互页后的验证方法是:缩放对比高德或天地图的边界走向,再用一个你熟悉的区县面积和属性表对照。我现在每次拿一份新的江苏shp地图,都会先跑一遍第一节的体检代码,确认坐标系、字段编码和几何有效性三个绿灯全亮才敢做下一步。这个习惯帮我省掉了无数次返工,希望帮到你。
本文还有配套的精品资源,点击获取