☰
2025海南省五级行政区划SHP数据:从省到村界的空间统计与转换实战
2026/10/26 19:08:32 网站建设 项目流程

简介:本资源为2025年海南省五级行政区划矢量数据包,面向从事GIS空间分析、城乡规划、资源管理与地理研究的科研人员、政府决策者及高校师生,可解决省、市、县、乡镇(街道)、社区(村界)多层级地理数据获取难的问题。压缩包共38个文件,约61.51MB,以shp、shx、dbf、prj等Shapefile核心格式为主,辅以sbx、sbn空间索引及cpg、xml元数据文件,覆盖省、市、县、乡镇、村界五个层级,可直接在ArcGIS、QGIS等软件中加载、绘制地图并开展空间分析。目前已有460人学习下载。数据层级完整、边界精度较高,适合用于宏观区域规划、土地利用分析、城乡扩张监测、灾害管理与基层精细化治理等场景,也可作为GIS教学与项目实践的基础底图,帮助读者快速构建海南省多尺度空间分析框架。

1. 从一份海南五级区划 SHP 说起:省到村界到底能拿来干什么

前阵子帮一个做农业遥感的朋友处理地块数据,他手里有海南全省的作物分布栅格,想按行政村统计种植面积,结果卡在最后一步——找不到带村界这一级的矢量底图。网上能搜到的海南行政区划数据,大多只到市县,少数到乡镇,村界这一级要么缺失,要么属性字段残缺,要么坐标系对不上。后来翻到这份 2025 海南省五级行政区划 SHP 数据,省、市、县、乡镇、村界五级齐全,才算把统计链路跑通。

这份资源的核心价值在于「层级完整」和「属性可用」。五级区划意味着你可以从省级一路下钻到村界,做多尺度的空间统计、制图或空间连接。适合做国土空间规划、农业估产、物流网点选址、应急资源调度这类需要精细行政单元的场景。如果你只是画个海南省轮廓图,那用不上它;但只要涉及「按村汇总」「按乡镇裁剪」「按市县做空间查询」,这份数据就是底图级别的存在。SHP 格式虽然老,但兼容性极强,ArcGIS、QGIS、GeoPandas、PostGIS 都能直接吃进去,这也是它至今仍是矢量数据交换主流格式的原因。

2. 五级区划 SHP 的字段结构与坐标系:拿到数据先别急着加载

2.1 五级图层拆分的逻辑与属性字段含义

这份数据按行政级别拆成五个独立图层,常见做法是省、市、县、乡镇、村界各一个.shp文件,配套.dbf、.shx、.prj等。每个图层的属性表里,核心字段通常包括行政区代码、名称、上级代码、几何类型。村界图层的字段最值得关注,因为它直接决定你能不能和外部数据做关联。

我一般拿到数据后先做三件事:看.prj确认坐标系,看.dbf字段类型和长度,看几何是否有多部件或自相交。村界数据因为采集精度问题,容易出现飞地、缝隙、重叠,这些在后续空间连接时都是坑。属性字段里,行政区代码是关联外部数据的钥匙,但要注意它可能是字符串型也可能是数值型,数值型会丢前导零,这是血泪经验。

图层常见字段字段类型注意点典型用途
省界省代码、省名称代码 2 位全省制图、宏观统计
市界市代码、市名称、省代码代码 4 位地级市汇总
县界县代码、县名称、市代码代码 6 位县域分析
乡镇乡镇代码、名称、县代码代码 9 位乡镇级统计
村界村代码、名称、乡镇代码代码 12 位村级精细统计

2.2 坐标系识别与投影转换的实操步骤

坐标系是矢量数据的第一道门槛。这份海南数据常见的是 CGCS2000 地理坐标系(EPSG:4490)或 WGS84(EPSG:4326),也可能带投影坐标系如 CGCS2000 3 度带。如果.prj文件缺失或乱码,可以用 QGIS 或 GeoPandas 读取后查看 CRS。

import geopandas as gpd # 读取村界图层,先看坐标系和字段 village = gpd.read_file("hainan_village.shp") print("CRS:", village.crs) print("字段:", village.columns.tolist()) print("几何类型:", village.geom_type.unique()) print("前 3 行:\n", village.head(3)) # 如果 CRS 为空,手动指定为 CGCS2000 地理坐标系 if village.crs is None: village = village.set_crs(epsg=4490) # 做面积统计前必须投影到等面积坐标系,否则面积单位是度,没有意义 village_proj = village.to_crs(epsg=4527) # CGCS2000 3 度带,海南适用 village_proj["area_km2"] = village_proj.geometry.area / 1e6 print(village_proj[["村名称", "area_km2"]].head())

这段代码的逻辑是:先读取并检查 CRS,缺失时手动指定,然后投影到等面积坐标系再算面积。参数上,EPSG:4490 是 CGCS2000 地理坐标,EPSG:4527 是 CGCS2000 3 度带投影,海南大致在 108°E 到 111°E 之间,3 度带带号对应 EPSG:4525 到 4528 区间,具体带号要看数据实际经度范围。如果投影带选错,面积会有系统性偏差,这个坑在跨带数据里特别常见。

提示:不要在地理坐标系下直接算面积或做缓冲区,单位是度,结果没有物理意义。先投影,再计算。

3. 用 GeoPandas 做村级空间统计:从加载到汇总的完整链路

3.1 空间连接与分组统计的代码实现

拿到五级区划后,最常见的操作是把外部点数据或栅格统计值挂到村界上。假设你有一份农户采样点 CSV,带经纬度,想统计每个村的采样点数量,核心就是空间连接加分组计数。

import pandas as pd import geopandas as gpd from shapely.geometry import Point # 读取村界并投影到适合的坐标系 village = gpd.read_file("hainan_village.shp").to_crs(epsg=4527) # 读取采样点 CSV,构造几何列 df = pd.read_csv("samples.csv") geometry = [Point(xy) for xy in zip(df["经度"], df["纬度"])] points = gpd.GeoDataFrame(df, geometry=geometry, crs="EPSG:4326").to_crs(epsg=4527) # 空间连接:每个点落在哪个村 joined = gpd.sjoin(points, village, how="left", predicate="within") # 按村分组计数 count_by_village = joined.groupby("村代码").size().reset_index(name="采样点数") print(count_by_village.head()) # 把统计结果挂回村界属性表 village_stats = village.merge(count_by_village, on="村代码", how="left") village_stats["采样点数"] = village_stats["采样点数"].fillna(0) village_stats.to_file("village_with_stats.shp", encoding="utf-8")

逻辑说明:sjoin的predicate="within"表示点必须完全落在村界多边形内部,边界上的点可能被漏掉,这是常见争议点。参数上,how="left"保留所有点,没匹配上的村代码为空。分组计数后merge回村界,fillna(0)把没有采样点的村补零。最后导出时指定encoding="utf-8",否则中文属性在 ArcGIS 里可能乱码。

3.2 多级汇总与面积加权计算

村级统计做完后,往往需要向上汇总到乡镇、县、市。如果只是简单求和,直接按上级代码分组即可;但如果涉及密度、比例这类指标,就需要面积加权。

# 从村界汇总到乡镇:先按乡镇代码分组求和 town_stats = village_stats.dissolve(by="乡镇代码", aggfunc={ "采样点数": "sum", "area_km2": "sum" }).reset_index() # 计算乡镇级采样密度 town_stats["采样密度"] = town_stats["采样点数"] / town_stats["area_km2"] # 继续汇总到县级 county_stats = town_stats.dissolve(by="县代码", aggfunc={ "采样点数": "sum", "area_km2": "sum" }).reset_index() county_stats["采样密度"] = county_stats["采样点数"] / county_stats["area_km2"] print(county_stats[["县代码", "采样点数", "area_km2", "采样密度"]])

dissolve是 GeoPandas 里做行政层级汇总的核心方法,它按指定字段合并几何并聚合属性。参数aggfunc决定各字段的聚合方式,计数类用sum,面积类也用sum,密度类必须重新计算而不是直接平均。这里有个容易翻车的地方:如果村界之间有重叠,dissolve后的面积会偏大,导致密度偏低。所以做汇总前最好先检查几何有效性。

注意:dissolve前建议执行village_stats = village_stats[village_stats.is_valid],剔除无效几何,否则合并结果可能异常。

4. 避坑与排查:五级区划 SHP 使用中的五个真实翻车记录

4.1 中文属性乱码:现象、原因与解决

现象:用 ArcGIS 打开 SHP,属性表里村名称显示为乱码或问号。原因:.dbf文件的默认编码是 GBK 或 GB2312,而 QGIS 或 GeoPandas 默认按 UTF-8 读取。解决:在 QGIS 里设置图层编码为 GBK,或用 GeoPandas 读取时指定encoding="gbk",导出时统一转成 UTF-8。我一般会在第一次读取后立刻另存为 UTF-8 版本,后续都用这个版本。

4.2 村界几何自相交与缝隙:现象、原因与解决

现象:做空间连接时,某些点明明在村界内却匹配不上,或者一个点匹配到多个村。原因:村界多边形存在自相交、缝隙或重叠,常见于不同来源数据拼接处。解决:用village.geometry.is_valid检查,对无效几何执行buffer(0)修复,对重叠区域用dissolve合并或按优先级去重。缝隙问题可以用snap或小缓冲容差处理,但容差不能太大,否则会改变边界。

4.3 坐标系误判导致面积偏差:现象、原因与解决

现象:算出来的村面积和官方公布数据差几倍。原因:把地理坐标系当成投影坐标系直接算面积,或者投影带选错。解决:先确认.prj内容,再用to_crs转到对应 3 度带或 6 度带。海南跨 109°E 到 111°E,3 度带带号 37 对应中央经线 111°E,EPSG 约 4527。如果数据跨带,需要分带处理或统一到 Albers 等面积投影。

4.4 行政区代码前导零丢失:现象、原因与解决

现象:村代码从 12 位变成 11 位或更少,和外部数据关联不上。原因:.dbf里代码字段是数值型,读取时前导零被自动去掉。解决:读取时用dtype={"村代码": str}强制字符串,或者在 Excel 里先把列格式设为文本再导入。如果已经丢了,可以用zfill(12)补零,但前提是知道原始位数。

4.5 导出 GeoJSON 后中文变 Unicode 转义:现象、原因与解决

现象:SHP 转 GeoJSON 后,用文本编辑器打开,中文变成\uXXXX。原因:GeoJSON 规范默认用 Unicode 转义,不是乱码。解决:用to_file(..., driver="GeoJSON")时加encoding="utf-8",或者用json.dumps(..., ensure_ascii=False)重新序列化。这个不算错误,但影响可读性,做 Web 地图时浏览器能正常解析。

5. 从 SHP 到 3D Tiles 与 WKT:几个高频转换场景的实操参数

5.1 SHP 转 3D Tiles 的预处理与工具链

村界数据做三维可视化时,常见需求是转成 3D Tiles 在 Cesium 里加载。SHP 不能直接转 3D Tiles,中间需要经过 GeoJSON 或 CityGML。我一般用ogr2ogr先转 GeoJSON,再用3d-tiles-tools或Cesium ion做切片。

# 第一步:SHP 转 GeoJSON,指定坐标系和编码 ogr2ogr -f GeoJSON -t_srs EPSG:4326 -lco ENCODING=UTF-8 village.geojson hainan_village.shp # 第二步:用 3d-tiles-tools 做切片(需先安装 Node.js 环境) npx 3d-tiles-tools createTileset -i village.geojson -o ./tileset --format b3dm

参数说明:-t_srs EPSG:4326把数据转到 WGS84 地理坐标,这是 Cesium 的默认坐标系。-lco ENCODING=UTF-8保证中文属性不乱码。切片时--format b3dm是 Batched 3D Model,适合带属性的矢量面。如果村界数据量很大,建议先按市县拆分再切片,否则单个 tileset 可能过大,加载卡顿。

5.2 SHP 转 WKT 与 TXT 的批量导出

有些数据库或接口只接受 WKT 格式的几何字符串,比如 PostGIS 的ST_GeomFromText。把 SHP 批量转 WKT 并导出 TXT,可以用 GeoPandas 一行搞定。

import geopandas as gpd village = gpd.read_file("hainan_village.shp", encoding="gbk") village["wkt"] = village.geometry.apply(lambda g: g.wkt) # 导出村代码、名称、WKT 到 TXT village[["村代码", "村名称", "wkt"]].to_csv("village_wkt.txt", sep="\t", index=False, encoding="utf-8") print("导出完成,共", len(village), "条")

逻辑说明:g.wkt把 Shapely 几何对象转成 WKT 字符串,to_csv用制表符分隔,方便后续导入数据库。参数上,sep="\t"避免 WKT 里的逗号和 CSV 分隔符冲突。如果 WKT 太长,某些数据库字段长度不够,需要先简化几何或改用 WKB。

5.3 渔网分割与村界裁剪的配合使用

做空间抽样或格网统计时,常需要生成渔网再和村界叠加。渔网分割 SHP 的常见做法是用 QGIS 的「创建网格」工具,或者用 GeoPandas 生成规则格网再裁剪。

import numpy as np from shapely.geometry import box # 获取村界范围 minx, miny, maxx, maxy = village.total_bounds cell_size = 1000 # 1km 格网 # 生成格网 cols = list(np.arange(minx, maxx, cell_size)) rows = list(np.arange(miny, maxy, cell_size)) cells = [box(x, y, x + cell_size, y + cell_size) for x in cols for y in rows] grid = gpd.GeoDataFrame({"geometry": cells}, crs=village.crs) # 用村界裁剪格网 grid_clipped = gpd.overlay(grid, village, how="intersection") print("裁剪后格网数:", len(grid_clipped))

参数说明:cell_size单位是米,因为已经投影到等面积坐标系。overlay的how="intersection"保留格网和村界的交集,每个格网会带上所在村的属性。如果格网太密,内存会爆,建议分县处理。

提示:渔网分割前务必确认投影坐标系,地理坐标系下cell_size单位是度,1 度约 111 公里,格网会大得离谱。

6. 验证数据完整性与批量处理的几个习惯

6.1 用拓扑检查验证五级区划的层级一致性

拿到五级数据后,我习惯先做一轮拓扑验证:村界合并后是否等于乡镇边界,乡镇合并后是否等于县界,以此类推。如果某一级合并后和上一级对不上,说明数据有缝隙或重叠。

# 村界合并后与乡镇边界对比面积 village_dissolved = village.dissolve(by="乡镇代码") town = gpd.read_file("hainan_town.shp").to_crs(village.crs) # 计算面积差异 village_dissolved["area_v"] = village_dissolved.geometry.area town["area_t"] = town.geometry.area compare = village_dissolved.merge(town[["乡镇代码", "area_t"]], on="乡镇代码") compare["diff_ratio"] = abs(compare["area_v"] - compare["area_t"]) / compare["area_t"] print(compare[compare["diff_ratio"] > 0.01][["乡镇代码", "diff_ratio"]])

这段代码找出面积差异超过 1% 的乡镇,这些就是需要重点检查的区域。差异可能来自坐标系不一致、几何精度损失或数据版本不匹配。我一般会把差异大的乡镇单独导出,在 QGIS 里叠加查看,确认是缝隙还是重叠。

6.2 批量裁剪与分县导出的脚本模板

如果只需要某个市县的数据,批量裁剪比手动选择高效得多。下面这个模板按县代码循环,每个县导出一个 SHP。

import os county_list = village["县代码"].unique() os.makedirs("output_by_county", exist_ok=True) for code in county_list: subset = village[village["县代码"] == code] out_path = f"output_by_county/{code}_village.shp" subset.to_file(out_path, encoding="utf-8") print(f"导出 {code}: {len(subset)} 个村")

逻辑说明:按县代码分组导出,文件名带县代码方便索引。参数上,encoding="utf-8"保证中文正常。如果数据量大,可以加chunksize或改用 GeoPackage 格式,避免 SHP 的 2GB 单文件限制。

6.3 一个让我改掉坏习惯的教训

早期我做村级统计时,直接拿地理坐标系的数据算面积,结果全县总面积比官方数据大了将近 30 倍,排查了一下午才发现是单位问题。从那以后,我每次拿到任何 SHP 数据,第一件事就是print(gdf.crs),第二件事是to_crs到等面积投影,第三件事才是打开属性表看字段。这个顺序帮我省掉了至少十次返工。希望这份海南五级区划数据和上面的处理思路,能帮你把村级尺度的空间分析链路一次跑通。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询