☰
1100万基础地理数据库县级行政区shp处理与空间分析实战
2026/9/25 18:01:54 网站建设 项目流程

简介:这份资源是2017年中国县级行政区划的矢量边界数据集,基于1:100万比例尺的1100万基础地理数据库整理,面向从事GIS分析、城市规划、人口统计、灾害评估等工作的技术人员与研究者,可用于大范围空间叠加与制图。压缩包共7个文件,约46.33MB,以SHP矢量格式为核心,配套DBF属性表存储县名与代码,PRJ定义CGS_WGS_1984坐标系,SHX、SBN、SBX等索引文件提升检索效率,XML则记录元数据信息,整体结构完整、开箱即用。目前已有614人学习下载。借助这套精细的县级行政边界,读者可将其与人口密度、地形、气候等数据叠加,快速完成区域统计、专题制图与空间模式挖掘,为规划决策提供可靠的地理底图支撑。

1. 1100万基础地理数据库里的县级行政区 shp:它到底是什么,谁该把它当回事

如果你手头拿到一份标注“1100万基础地理数据库_2017县级行政区shp文件”的数据,第一反应大概率不是兴奋,而是犯嘀咕:这跟网上随手能下的全国区划 shp 有啥区别,值不值得花时间清洗入库。先说结论,1100万指的是比例尺 1:1,000,000,属于中小比例尺的基础地理数据库,一套完整的库通常按要素分层组织,县级行政区只是其中“政区与境界”这一层。2017 这个年份意味着它的行政区划快照停在那一年,之后发生的撤县设市、区划调整它一概不知。它解决的核心问题是:当你需要一套全国范围、拓扑相对干净、属性字段规整的县级面数据做底图、做统计挂接、做空间汇总时,它比很多来路不明的 shp 靠谱。适合谁?做全国尺度专题制图、做人口经济数据空间化、做流域与行政区叠加分析的人。如果你只关心某一个市甚至某一个县,这份数据的分辨率可能不够用,别硬上。

2. 先搞懂 1100万县级行政区 shp 的字段与坐标,再谈怎么用

2.1 打开文件先看这三样:坐标系、字段、几何类型

拿到 shp 别急着往 ArcGIS Pro 里拖,先确认三件事,顺序不能乱。第一是坐标系,1100万基础地理数据库常见做法是地理坐标系 CGCS2000,单位是度,不是米。你要是直接拿它算面积,得到的是平方度,毫无意义。第二是字段结构,县级行政区层一般会有行政区代码、名称、上级代码这类属性,但不同来源的库字段命名差异很大,有的叫 XZQDM,有的叫 ADCODE,得先看一眼再写代码。第三是几何类型,县级行政区是面,但偶尔会遇到 MultiPolygon,比如沿海县带岛屿,处理时要注意。

用 Python 的 geopandas 快速体检,比在桌面软件里点来点去快得多:

import geopandas as gpd # 读取 shp,注意 encoding 参数,中文属性乱码多半是这里没设对 gdf = gpd.read_file("county_2017.shp", encoding="utf-8") # 看坐标系 print("CRS:", gdf.crs) # 看字段名和类型 print(gdf.dtypes) # 看几何类型分布 print(gdf.geom_type.value_counts()) # 看前几行属性 print(gdf.head())

这段代码的逻辑是先建立数据认知再动手。encoding="utf-8"是关键,很多老库用 GBK,读出来属性全是问号,换成encoding="gbk"再试。gdf.crs如果返回 None,说明 shp 丢了 prj 文件,你得手动指定坐标系,否则后续所有空间操作都是错的。geom_type里如果出现 GeometryCollection,说明有脏几何,得先修复。

2.2 投影转换:为什么算面积前必须换到投影坐标系

地理坐标系下做缓冲区、算面积、算长度,结果都是错的,这是新手最容易翻车的地方。1100万数据做全国分析,常用的是 Albers 等积投影,因为它能保证面积不变形。转换代码如下:

# 从地理坐标系转到 Albers 等积投影,中央经线 105 度,双标准纬线 25 和 47 gdf_proj = gdf.to_crs("+proj=aea +lat_1=25 +lat_2=47 +lat_0=0 +lon_0=105 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs") # 现在算面积才是平方米 gdf_proj["area_km2"] = gdf_proj.geometry.area / 1e6 print(gdf_proj[["NAME", "area_km2"]].head())

参数说明:lat_1和lat_2是双标准纬线,中国常用 25 和 47,能覆盖大部分国土;lon_0=105是中央经线,大致在中国中部;units=m保证输出单位是米。如果你只做某一个省,中央经线可以改成该省中心经度,变形更小。转完之后面积字段才有物理意义,拿它跟统计年鉴的县域面积对一下,差太多说明投影或数据本身有问题。

2.3 属性挂接:把统计表格拼到 shp 上的正确姿势

县级行政区 shp 最大的用途之一是挂接统计数据,比如 GDP、人口。挂接的关键是行政区代码要能对上。常见坑是代码类型不一致,shp 里是字符串,Excel 里是数字,直接 merge 全变 NaN。做法是两边都转成字符串再拼:

import pandas as pd # 读统计表,行政区代码列强制为字符串 stats = pd.read_excel("county_stats.xlsx", dtype={"行政区代码": str}) # shp 里的代码列也转字符串,去掉可能存在的空格 gdf["XZQDM"] = gdf["XZQDM"].astype(str).str.strip() # 左连接,保留所有县级单元 merged = gdf.merge(stats, left_on="XZQDM", right_on="行政区代码", how="left") # 检查有多少没匹配上 print("未匹配数量:", merged["行政区代码"].isna().sum())

逻辑说明:dtype={"行政区代码": str}防止 pandas 把带前导零的代码读成数字。str.strip()处理肉眼看不见的空格。how="left"保证 shp 的几何不丢。未匹配数量如果很大,要么是代码版本不一致,要么是 2017 年之后区划变了,这份数据对不上新表,这时候你得找区划变更对照表,而不是硬凑。

3. 用 1100万县级 shp 做空间分析:从筛选到叠加的完整链路

3.1 按属性筛选目标县,再按位置筛选流域范围

实际项目里很少直接用全国数据,都是先缩小范围。比如你要研究塔里木河流域涉及的县,思路是先按流域边界做空间筛选,再按属性挑县。这里涉及两个操作:属性筛选和空间筛选。

# 属性筛选:挑出某几个省的县 target = gdf_proj[gdf_proj["省代码"].isin(["65", "63"])] # 空间筛选:用流域边界裁剪,得到流域内的县 basin = gpd.read_file("tarim_basin.shp").to_crs(gdf_proj.crs) counties_in_basin = gpd.overlay(target, basin, how="intersection") # 按面积占比判断哪些县主体在流域内 counties_in_basin["ratio"] = counties_in_basin.geometry.area / counties_in_basin["area_km2"] / 1e6 main_counties = counties_in_basin[counties_in_basin["ratio"] > 0.5]

参数说明:isin里的代码是省级行政区代码前两位,65 是新疆,63 是青海,按需替换。overlay的how="intersection"表示求交集,会切碎几何,所以后面用面积占比还原“哪些县主体在流域内”。ratio > 0.5是经验阈值,低于这个值的县可能只有边角被流域碰到,纳入分析会引入噪声。这一步做完,你就得到了一份干净的、针对特定流域的县级单元列表。

3.2 用渔网分割 shp:把县级面切成规则格网做统计

有时候你需要把县级行政区切成更小的格网,比如做人口密度格网化。渔网分割 shp 是高频操作,ArcGIS Pro 里有工具,但用 Python 更可控。思路是先给每个县生成渔网,再跟县面求交。

import numpy as np from shapely.geometry import box def make_fishnet(gdf, cell_size=10000): """给每个要素生成 cell_size 米边长的渔网并求交""" results = [] for idx, row in gdf.iterrows(): minx, miny, maxx, maxy = row.geometry.bounds # 生成格网中心点 xs = np.arange(minx, maxx, cell_size) ys = np.arange(miny, maxy, cell_size) cells = [box(x, y, x + cell_size, y + cell_size) for x in xs for y in ys] fishnet = gpd.GeoDataFrame(geometry=cells, crs=gdf.crs) # 只保留与县面相交的格网 clipped = gpd.overlay(fishnet, gpd.GeoDataFrame([row], crs=gdf.crs), how="intersection") clipped["county_code"] = row["XZQDM"] results.append(clipped) return pd.concat(results, ignore_index=True) grid = make_fishnet(gdf_proj.head(5), cell_size=10000)

逻辑说明:bounds拿到每个县的外包矩形,np.arange按步长生成格网坐标,box造矩形。overlay求交后每个格网只保留落在县内的部分。cell_size=10000是 10 公里格网,全国尺度常用 1 公里到 10 公里,看你的数据量和精度需求。这个函数对全国数据会很慢,实际用的时候建议先按省拆分再并行,或者直接用 PostGIS 的ST_SquareGrid,效率高一个量级。

3.3 导出与格式转换:shp 转 GeoJSON、KML 和 3D Tiles 的取舍

做完分析要交付,格式选择有讲究。shp 字段名限制 10 个字符,中文支持差,导出 GeoJSON 更通用;要给非 GIS 人员看,KML 在 Google Earth 里直接打开;要做三维可视化,shp 转 3D Tiles 是另一条链路。

# 导出 GeoJSON,注意 ensure_ascii=False 保留中文 merged.to_file("county_merged.geojson", driver="GeoJSON", encoding="utf-8") # 导出 KML,需要先转成 EPSG:4326 merged.to_crs("EPSG:4326").to_file("county.kml", driver="KML")

参数说明:GeoJSON 用encoding="utf-8",KML 必须用 WGS84 经纬度,否则 Google Earth 里位置会偏。shp 转 3D Tiles 不是 geopandas 能直接干的,常见做法是先把 shp 转成 GeoJSON,再用 Cesium 的工具链或专门转换器处理,注意高度字段要单独指定,否则所有建筑贴地。如果你的数据只是行政区面,没有高度信息,转 3D Tiles 意义不大,不如直接发布成矢量切片。

4. 避坑与排查:1100万县级 shp 处理中最容易翻车的 5 个地方

4.1 中文属性乱码,改 encoding 没用怎么办

现象:读 shp 后属性表里中文全是乱码,换了几种 encoding 都不对。原因:shp 的 dbf 文件编码不是标准 UTF-8 或 GBK,可能是 GB2312 甚至更老的编码,或者文件本身被二次编辑过。解决:先用gpd.read_file不带 encoding 读一次,看乱码规律;再用chardet检测 dbf 文件编码;实在不行用 QGIS 打开,QGIS 的编码识别更宽容,在里面重新导出为 UTF-8 的 GeoJSON,再进 Python 处理。血泪经验是别在编码上死磕,转一道手往往更快。

4.2 面积算出来跟年鉴对不上,差了好几倍

现象:投影转换后算的县面积,跟统计年鉴差 2 到 3 倍。原因:大概率是投影参数不对,或者原始数据本身是 1100万这种小比例尺,几何经过了综合简化,面积精度本来就有限。解决:先确认投影参数,中国全国用 Albers,中央经线 105;再拿一个已知面积的县做基准测试;如果还是差很多,接受这份数据不适合做精确面积统计,它更适合做位置参考和空间关系分析。要精确面积,用更大比例尺的数据。

4.3 merge 之后行数变多,几何重复了

现象:挂接统计表后,原本 2800 多个县变成了 3000 多行。原因:统计表里有重复的行政区代码,或者 shp 里同一个代码对应多个几何要素(比如飞地)。解决:merge 前先stats.drop_duplicates(subset=["行政区代码"]),shp 这边用dissolve按代码合并几何。如果确实有飞地需要保留,那就接受多行,但统计值挂接时要注意别重复计算。

4.4 用 overlay 裁剪后,边界上出现碎屑多边形

现象:流域裁剪县级 shp 后,结果里有很多面积几乎为零的碎多边形。原因:两个数据层的边界不完全重合,叠加时产生拓扑碎片。解决:裁剪后按面积过滤,gdf[gdf.geometry.area > 阈值],阈值根据你的最小关注单元定,比如 1e6 平方米。更彻底的做法是裁剪前先做一次buffer(0)修复几何,或者用snap把边界对齐。

4.5 导出 KML 后 Google Earth 打不开或位置偏移

现象:KML 文件生成了,但 Google Earth 里要么报错,要么图形跑到海里去了。原因:坐标系没转成 WGS84,或者几何有自相交。解决:导出前强制to_crs("EPSG:4326"),并用gdf.geometry.is_valid检查,无效的用buffer(0)修复。另外 KML 对字段名也有要求,中文字段名可能被截断,导出前把字段名改成英文。

5. 把 2017 县级 shp 用出长期价值:版本管理与自动化更新

这份数据是 2017 年的快照,但你的项目可能持续好几年。我一般会做两件事让它不过期。第一是建立区划变更对照表,把 2017 年之后撤县设市、改名、调整隶属的条目手工维护成一张 CSV,每次挂接统计前先跑一遍对照,把旧代码映射到新代码。第二是把整个处理链路脚本化,从读取、投影、挂接、裁剪到导出,写成一个可重复执行的 pipeline,数据更新时只换输入文件,参数不动。

# 区划变更对照表示例结构 # old_code, new_code, change_type, change_year # 130000, 130100, 撤县设区, 2018 def apply_code_mapping(gdf, mapping_csv): mapping = pd.read_csv(mapping_csv, dtype=str) gdf["XZQDM"] = gdf["XZQDM"].astype(str) # 用 map 做替换,没有映射的保留原值 gdf["XZQDM_NEW"] = gdf["XZQDM"].map( dict(zip(mapping["old_code"], mapping["new_code"])) ).fillna(gdf["XZQDM"]) return gdf

这段代码的逻辑是用字典映射做代码替换,fillna保证没变更的县不受影响。参数上,mapping_csv要维护好变更年份,做时间序列分析时按年份筛选映射关系,别一股脑全替换。验证方法是替换后统计代码数量,跟最新区划代码表对一遍,数量对不上就说明映射有遗漏。

最后一个习惯:每次处理完 shp,我都会把中间结果存成 GeoPackage 而不是 shp,因为 GeoPackage 单文件、支持长字段名、支持中文、支持多图层,比 shp 省心太多。1100万这份数据本身质量不错,但它的价值取决于你怎么管它、怎么接后续数据。别把它当成一次性下载完就扔的素材,把它当成一个需要维护的基础图层,你的项目会少很多返工。希望帮到你。

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

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

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

立即咨询