
简介江苏省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, encodingutf-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 到 WGS843.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, encodinggbk) # 先确认原始坐标系 print(原始坐标系:, gdf.crs) # 转到WGS84地理坐标系方便Web底图叠加 gdf_wgs84 gdf.to_crs(epsg4326) # 查看转换后边界范围 print(转换后边界范围:, gdf_wgs84.total_bounds) # 另存为GeoJSONWeb前端直接用 gdf_wgs84.to_file(jiangsu_wgs84.geojson, driverGeoJSON)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(epsg4326)手动指定坐标系是高风险操作如果原始数据其实是西安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(tolerance0.005) # 简化后修复无效几何 gdf_simplified[geometry] gdf_simplified.geometry.buffer(0) # 导出轻量级GeoJSON gdf_simplified.to_file(jiangsu_simplify.geojson, driverGeoJSON) 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, encodinggbk) # 读取业务统计表 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, onadcode, howleft) 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(columnvalue, axax, cmapOrRd, legendTrue, edgecolorblack, linewidth0.5, missing_kwds{color: lightgrey, label: 暂无数据}) # 叠加地市级边界线加粗显示 gdf_boundary merged.dissolve(bycity_code) gdf_boundary.boundary.plot(axax, colorblack, linewidth1.2) plt.axis(off) plt.show()dissolve按地市代码合并内部区县边界生成地市轮廓线加粗后让读者一眼看出层级结构。edgecolor和linewidth控制面边界如果觉得县界太密就只保留地市边界把区县边界线宽设为0.2或直接省略。4.3 飞地与嵌套关系画区县图时常用的双层图层组织江苏部分区县的行政区划存在飞地也就是一块区域归A管辖但地理位置在B辖区内部。shp里飞地通常作为一个独立要素存在如果只是简单按区县名字分组画出来会出现叠加覆盖。处理飞地我一般会先用区域代码分组再检查每个分组内的几何数量。# 统计每个区县的几何要素数量排查多几何 geo_count gdf.groupby(adcode).size().reset_index(namecount) print(存在多个几何要素的区县:, geo_count[geo_count[count] 1])如果某个区县有多个要素那要么是飞地要么是岛屿要么是数据分割问题。可视化时建议将它们合并成一个MultiPolygon再映射填色避免颜色被覆盖或统计面积偏大。合并操作使用dissolve即可会按指定字段把同一个区县的所有几何合并。5. 江苏shp地图避坑属性乱码、边界漂移与区划过时怎么排查5.1 属性表乱码现象读取shp后name字段显示为“”或一堆乱码。原因属性表.dbf文件是老版本编码常见为GBK或GB2312但读取时用了utf-8。解决读取时指定encodinggbk如果依旧乱码逐个尝试gb2312和utf-8-sig最稳妥的是用文本编辑器打开shp同目录下的.cpg文件查看声明编码。我有一次遇到一个混合编码的属性表字段名是utf-8但字段值是gbk只能先分字段重新编码转成统一格式再入库。gdf gpd.read_file(jiangsu.shp, encodinggbk) # 确认名称字段是否正常 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, onadcode) # 创建地图中心点放在江苏中部 m folium.Map(location[33.0, 119.5], zoom_start7) folium.Choropleth( geo_datagdf_json, datagdf_merged, columns[adcode, value], key_onfeature.properties.adcode, fill_colorYlOrRd, fill_opacity0.7, line_opacity0.5, legend_name业务指标 ).add_to(m) m.save(jiangsu_interactive.html)这段代码里有两个关键参数需要你根据实际字段调整key_on的值要匹配GeoJSON属性字段名columns的第一列必须是shp和业务表共用的键。folium的Choropleth颜色区间默认按分位数分桶适合查看相对分布。如果你需要支持点击弹窗显示区县名称和指标值可以在GeoJSON上叠加一层GeoJsonTooltip把我这个基础版本继续扩展。做完这个交互页后的验证方法是缩放对比高德或天地图的边界走向再用一个你熟悉的区县面积和属性表对照。我现在每次拿一份新的江苏shp地图都会先跑一遍第一节的体检代码确认坐标系、字段编码和几何有效性三个绿灯全亮才敢做下一步。这个习惯帮我省掉了无数次返工希望帮到你。本文还有配套的精品资源点击获取