简介这份2004年中国行政区划Shapefile数据集面向历史地理研究、社会经济分析及地图制图人员提供省、地级市、县三级行政边界的矢量记录可用于GIS空间分析、历史区划复原与专题图制作。压缩包共21个文件约6.92MB以shp、dbf、prj、shx、sbn、sbx及xml为主shp承载行政边界几何dbf存储名称、代码、人口、面积等属性prj定义空间参考系shx与sbn/sbx为空间索引以提升读取效率xml记录元数据。已有438人学习下载。需注意该数据为2004年版本与现行区划存在差异部分地级市名称已变更海南省个别地级市当时尚未纳入使用时宜结合更新数据校正。资源可与ArcGIS、QGIS等软件兼容便于叠加人口、经济指标开展历史空间分布与变化趋势分析适合作为历史区划研究的基础底图与教学演示素材。1. 2004 版行政区划 shp 到底能干什么先看清它的边界手里有一份 2004 年的中国行政区划 shp 文件很多人第一反应是“太老了还能用吗”。这个问题得拆开看。行政区划数据不是地图底图它的价值在于“某一时刻的行政边界快照”——省、市、县三级界线、行政代码、名称全部固化在 2004 年那个时间点上。你要做的是历史对比、教学演示、老数据回溯、或者给一批 2004 年前后的统计年鉴配空间底图这份数据就是对的你要做的是当前行政区划查询、最新邮编匹配、实时区县变更追踪它一定翻车。所以第一件事不是打开 ArcGIS 看效果而是想清楚你的分析时间锚点在哪一年。这份资源适合做空间分析入门、历史行政区划研究、老统计资料可视化的人也适合需要一份“干净、结构简单、字段少”的 shp 来练手 GIS 流程的从业者。它不适合拿来做现势性要求高的业务系统底图这一点先立住后面所有操作才有意义。2. 把 shp 读进工作流字段、坐标系与编码三件事2.1 先验字段结构别急着画图拿到一个 shp 压缩包我一般不会直接双击打开。先解压看目录里是不是完整的.shp、.shx、.dbf、.prj四件套。2004 年前后的行政区划数据常见字段就那么几个省级名称、地市名称、县级名称、行政区划代码有的版本还会带一个面积字段。字段名可能是英文缩写也可能是拼音甚至直接是中文这取决于当年制作者的导出习惯。用 Python 的geopandas先读一遍把字段名、数据类型、记录数打出来比在软件里点属性表快得多。import geopandas as gpd # 读取 shp注意指定编码2004 年前后的 dbf 常见 GBK gdf gpd.read_file(china_admin_2004.shp, encodinggbk) print(记录数:, len(gdf)) print(字段列表:, list(gdf.columns)) print(几何类型:, gdf.geom_type.unique()) print(坐标系:, gdf.crs) print(gdf.head(3))这段代码的重点不在读而在“验”。记录数能告诉你这份数据是省级、市级还是县级粒度geom_type如果混着Polygon和MultiPolygon说明有些行政区被拆成了多部件后面做面积统计或空间连接时要先统一crs如果是None说明.prj缺失或没被识别那就得手动补坐标系否则量出来的面积全是错的。字段名如果是乱码八成是编码问题gbk不行就换gb18030再试别硬改字段。2.2 坐标系这件事2004 年的数据大概率没有 .prj这是血泪经验里最常见的一条。早年流出的行政区划 shp很多只给了几何没给投影文件。你打开一看图形是正常的因为经纬度坐标本身就能画出来但一旦做缓冲区、算面积、做距离分析单位就变成了“度”结果毫无意义。判断方法很简单看坐标数值范围。经度在 73 到 135 之间、纬度在 3 到 54 之间基本可以确定是 WGS84 地理坐标系。确认之后第一件事是补上 CRS再做投影转换。# 若 crs 为空先按经纬度补 WGS84 if gdf.crs is None: gdf gdf.set_crs(EPSG:4326) # 转成适合全国尺度面积统计的等积投影Albers gdf_albers gdf.to_crs(EPSG:3857) # 仅作快速预览 gdf_area gdf.to_crs(projaea lat_125 lat_247 lat_00 lon_0105 datumWGS84 unitsm) gdf_area[area_km2] gdf_area.geometry.area / 1e6这里EPSG:3857只适合看图形不适合算面积因为它在高纬度会严重变形。真正做全国面积统计常见做法是 Albers 等积投影中央经线取 105 度标准纬线取 25 和 47这套参数在国内区域分析里用得很多。参数不是死的你做省级以下分析时可以把中央经线调到所在省的中部变形会更小。补完 CRS 再算面积和直接拿经纬度算结果能差出百分之几十这不是玄学是投影的锅。2.3 编码与名称字段的清洗顺序2004 版数据里行政区划代码通常是 6 位或 12 位。6 位是县及县以上常用的编码结构前两位省、中间两位市、后两位县。但老数据经常出现代码位数不齐、名称带空格、名称里混着“市辖区”“省直辖县级行政区划”这类占位名的情况。清洗顺序我一般固定为先去空格再统一代码为字符串并补零最后处理占位名称。# 代码统一为 6 位字符串避免前导零丢失 gdf[code] gdf[code].astype(str).str.strip().str.zfill(6) # 名称去空格占位名单独标记 gdf[name] gdf[name].astype(str).str.strip() placeholder gdf[name].str.contains(市辖区|省直辖|自治区直辖, naFalse) print(占位名称记录数:, placeholder.sum())补零这一步特别容易翻车。如果代码列被读成整数110101会变成110101没问题但010101这种前导零直接消失后面按代码关联统计年鉴就对不上。统一转字符串再zfill(6)是最稳的。占位名称不要急着删先标记出来因为它们在省级汇总时往往需要合并回上级删了反而丢面积。3. 从 shp 到可用底图分级汇总与空间连接3.1 省、市、县三级怎么拆一份县级粒度的 shp本身就能往上汇总出市级和省级。做法不是重新找数据而是按代码前缀分组做几何融合。6 位代码里前两位是省前四位是市用dissolve按前缀合并比手工选要素靠谱得多。这里要注意融合会丢掉原始字段所以融合前先把需要保留的名称字段整理好。# 省级按代码前两位融合 gdf[prov_code] gdf[code].str[:2] prov gdf.dissolve(byprov_code, aggfunc{name: first}).reset_index() # 市级按代码前四位融合 gdf[city_code] gdf[code].str[:4] city gdf.dissolve(bycity_code, aggfunc{name: first}).reset_index() print(省级要素数:, len(prov), 市级要素数:, len(city))dissolve的aggfunc决定融合后保留哪个名称用first只是取组内第一条严格说应该按行政级别取上级名称但 2004 版数据里县级名称字段未必带上级全称所以更稳的做法是融合后单独维护一张代码到名称的对照表。省级要素数可以用来做一次 sanity check2004 年前后省级单元数量是固定的如果融合出来多出几个多半是代码前缀有异常值比如代码为空或位数不对回头查那几条记录就行。3.2 和统计表格做空间连接时的两个参数行政区划 shp 最常见的用途是把一批带行政区代码或名称的统计数据挂到地图上。如果统计表里有标准代码直接按代码merge不要用空间连接因为空间连接在边界调整过的年份容易把点落到隔壁县。只有统计表里只有点位坐标、没有行政代码时才用sjoin。用sjoin时两个参数必须想清楚predicate选within还是intersectshow选left还是inner。# 统计表带代码直接属性连接 stats gpd.read_file(stats_2004.csv) # 假设含 code 列 merged gdf.merge(stats, oncode, howleft) # 只有点位空间连接点必须落在面内 points gpd.read_file(points.shp).to_crs(gdf.crs) joined gpd.sjoin(points, gdf[[code, name, geometry]], howleft, predicatewithin)predicatewithin要求点严格落在面内部边界上的点会被判为不匹配intersects则把边界点也算进去。做行政区归属时我一般用within因为边界点归属本身就有歧义宁可留空也不要错挂。howleft保证没匹配上的点也保留方便你回头查是坐标偏了还是行政区缺失。连接完先看index_right的空值比例空太多说明坐标系没对齐这是比字段错误更隐蔽的坑。3.3 导出前的几何有效性检查老 shp 的几何问题比新数据多自相交、重复点、零面积多边形都可能出现。导出成 GeoJSON 或入库之前跑一遍is_valid把无效几何单独拎出来修。buffer(0)是常用的修复手段但它会改变几何形状只适合做展示底图不适合做精确面积统计。invalid gdf[~gdf.geometry.is_valid] print(无效几何数:, len(invalid)) # 展示用途可尝试 buffer(0) 修复 gdf_fixed gdf.copy() gdf_fixed[geometry] gdf_fixed.geometry.buffer(0) print(修复后无效数:, (~gdf_fixed.geometry.is_valid).sum())修复前后都要记录数量修复后如果还有无效的说明问题不是简单自相交可能是环方向错误或坐标精度问题这时候别硬修单独导出那几条人工看。导出格式上shp 有字段名长度限制中文名容易截断做 Web 底图时转 GeoJSON 或 GeoPackage 更省心字段名提前改成英文短名。4. 避坑清单这份 2004 数据最容易翻车的五个地方4.1 现象面积算出来比实际大好几倍原因几乎都是没做投影转换直接拿经纬度坐标算面积单位是“平方度”。解决方法是先确认 CRS再转 Albers 等积投影后计算参数按分析范围调整全国尺度用中央经线 105 度那套省级以下可以换本地中央经线。4.2 现象按代码关联统计表匹配率不到一半原因通常是代码列被读成整数导致前导零丢失或者统计表代码是 12 位而 shp 是 6 位。解决方法是两边都统一转字符串、去空格、按较短位数截取后再关联关联前先各打印前十条对比格式别等 merge 完才发现对不上。4.3 现象地图上某些县缺一块或者多一块原因是原始数据里存在多部件几何或者相邻行政区边界不共享、有缝隙重叠。解决方法是先explode拆多部件看分布再用unary_union检查重叠展示用途可以接受微小缝隙做面积汇总时必须先做拓扑修复否则汇总面积对不上统计口径。4.4 现象中文属性在 QGIS 里显示乱码原因是 dbf 编码和软件默认编码不一致2004 年前后的数据多为 GBK。解决方法是在读取时显式指定encodinggbkQGIS 里在图层属性中改数据源编码导出时统一转 UTF-8别在乱码状态下直接改字段值改完再转码会二次损坏。4.5 现象空间连接后大量点匹配不到行政区原因多半是点位坐标系和 shp 坐标系不一致或者 shp 本身 CRS 缺失导致to_crs没生效。解决方法是连接前分别打印两者的crs和坐标范围确认都在同一经纬度区间后再做sjoin匹配不上的点单独导出看落点别直接丢。5. 进阶用法把 2004 版当成时间切片做对比分析这份数据真正有意思的用法不是单独看而是当成一个时间切片和另一年份的行政区划数据做对比。做法上先把两个年份的数据都统一到同一投影、同一代码位数然后按代码做外连接用几何差异判断边界是否变化。代码能对上的比较几何是否完全一致代码对不上的说明期间发生了撤并或新增这部分单独列出来人工核对。对比结果不要只看数量要看空间分布——哪些区域调整密集往往和当年的区划调整节奏有关。# 假设 gdf_2004 与 gdf_other 均已统一 CRS 和 6 位代码 cmp gdf_2004[[code, name, geometry]].merge( gdf_other[[code, name, geometry]], oncode, howouter, suffixes(_04, _xx), indicatorTrue ) print(cmp[_merge].value_counts()) # 两边都有的比较几何是否一致 both cmp[cmp[_merge] both].copy() both[same_geom] both.apply( lambda r: r[geometry_04].equals(r[geometry_xx]), axis1 ) print(几何未变数量:, both[same_geom].sum())indicatorTrue会生成_merge列left_only和right_only就是两个年份之间新增或消失的单元这部分是区划调整的核心线索。equals比较的是几何是否完全一致精度差异也会被判为不同所以实际用的时候可以先做一次坐标精度截断或者用面积差比例做阈值判断别被浮点误差带偏。对比结果导出成表配上变化前后的名称就是一份能直接看的区划变更清单。我自己的习惯是每次拿到年份较早的行政区划数据先跑一遍 CRS 检查、代码补零、几何有效性这三步再谈任何分析。这三步不过后面画出来的图再漂亮也是错的。这份 2004 版 shp 的价值不在“新”而在“准地定格了一个时间点”用对场景、把住坐标系和编码两道关它能撑起不少历史空间分析的需求。希望帮到你。本文还有配套的精品资源点击获取