
简介这套中国历史朝代行政范围矢量数据集依据谭其骧《中国历史地图集》及历代正史地理志等权威文献数字化重构覆盖夏商周至明清共28个主要政权或朝代阶段面向历史地理研究者、GIS开发者和高校教学场景解决历史政区边界数字化对标问题。压缩包共178个文件约38.08MB核心为shp矢量文件配套dbf属性表、prj坐标系、xml元数据及说明文档可直接导入主流GIS平台。已有234人浏览学习。每个朝代独立成层包含省府县三级行政单位多边形属性表含37项结构化字段如建置时间、治所经纬度、古今地名对照、史料出处、边界置信等级等经过拓扑检查支持叠加分析、时序动画与空间统计适用于历史疆域演变、军事防御体系、丝绸之路路线复原等研究。1. 中国历史朝代行政范围矢量SHP这包数据到底怎么用起来先说一个反直觉的结论大多数人拿到「中国历史朝代行政范围矢量SHP」这类数据集都会先去打开属性表但真正决定这包数据能不能用的不是里面有多少个朝代而是坐标系统、属性编码和几何拓扑这三个最不起眼的环节。你下载的数据集如果覆盖秦到清的主要历史行政区划理论上能做很多事但如果你直接把它拖进 QGIS 或者 ArcMap看到的可能是乱码、错位、甚至无法显示的空白图层。这不是数据本身没价值而是你还没掌握历史行政区划数据的地层逻辑。这篇文章我会从数据组织、加载预处理、筛选制图和避坑排查四个角度完全按照一线实操的方式来拆解这份资源帮你弄清楚它包含什么、能解决什么、以及哪些坑你一定会遇到。2. SHP数据集的结构与坐标系统先搞清几何和属性到底存了什么2.1 属性表里的字段逻辑朝代、年份与政区名拿到手的第一步不是急着加载而是先打开属性表搞清楚每一个字段的含义。这类历史行政区划 SHP 数据集的属性表通常包含几个核心字段朝代名称dynasty、政权名称regime、起止年份start_year 和 end_year以及行政级别level和政区名称name。不同朝代的数据粒度差异极大比如秦代可能只有郡级边界而唐代会细化到州、府级别明代可能还区分布政使司和都司卫所。你首先要确认的是你手里的这份资源到底碎到什么程度因为这会直接影响后续的图层叠加和统计口径。在看属性表的时候最容易翻车的是编码问题。很多历史数据是从古籍地图数字化来的属性字段用的是 GBK 或者 GB2312 编码而 QGIS 默认用 UTF-8 读取于是你打开属性表看到的就是一堆「」符号。这时候先别急着给数据判死刑用 QGIS 的「数据源管理器」重新设置编码为 GBK 就能正常显示。如果手头没有 GIS 软件也可以用 Notepad 直接打开 DBF 文件注意不是打开 SHP 主文件把编码从 ANSI 转为 UTF-8然后再放回原始目录。我一般会先用 Python 的dbfread库快速检测一下 DBF 的编码字节避免在 GUI 里反复试错。import dbfread # 读取属性表打印前两条记录检查字段和值 table dbfread.DBF(han_administration.dbf, encodinggbk) for i, rec in enumerate(table): if i 2: break print(rec)这段代码假设你的 DBF 文件编码是 GBK如果打印出来仍然乱码就把 encoding 换成 utf-8 再试。dbfread不会修改原文件所以你可以安全地对同一文件多次尝试。实际项目中我更推荐在加载前就确认编码因为 DBF 一旦转错编码再转回来时部分汉字可能永久损坏。2.2 坐标系为什么图层总是跑到非洲海边历史行政区划数据的坐标系是另一个大坑。绝大多数这类 SHP 数据集的几何字段是基于经纬度存储的地理坐标系常见的是 WGS84EPSG:4326。但也有一部分来源会默认使用 GCS_Beijing_1954EPSG:4214或者甚至是一个未定义的投影坐标系。当你的图层投影与底图不一致时地图上的边界可能飞到大西洋或者非洲中部的海面上。判断方法很简单在 QGIS 里右键图层打开「属性—源」查看「坐标参考系统」。如果显示的是未知Unknown CRS那么你需要在图层属性里手动指定一个合理的坐标系。最稳妥的做法是一律按 WGS84 处理因为现代底图如 OpenStreetMap、Google 卫星图都基于 WGS84 经纬度。如果你发现数据本身是用 GCS_Krasovsky_1940 或类似的老基准面定义的那就需要用ogr2ogr做一次坐标转换把几何从老基准面转到 WGS84 上否则即使你强行指定 EPSG:4326图层在底图上也会存在至少 100 米到 300 米的偏移。对于历史地理研究的尺度这种偏移可能还能容忍但如果你要叠加现代河流、道路或城市边界做精确分析就必须认真处理基准面转换不能只改 EPSG 号了事。ogr2ogr -t_srs EPSG:4326 -s_srs EPSG:4214 han_4326.shp han_4214.shp这条命令的含义是从 EPSG:4214老北京基准面经纬度转换到 EPSG:4326WGS84输出新文件han_4326.shp。其中-s_srs参数告诉工具输入数据的坐标系-t_srs指定输出坐标系。如果你的数据没有定义坐标系你需要先通过周围地理要素的分布特征比如海岸线形状、河流相对位置来推断一个近似坐标系统然后在转换时用-a_srs强行指定例如ogr2ogr -t_srs EPSG:4326 -a_srs EPSG:4214。这种推断有一定玄学成分最好的鉴别方式是把图层叠加到现代行政边界上看海岸线和主要河流是否吻合再决定是否需要微调。2.3 几何类型与拓扑结构面还是多部分面历史行政区划数据的几何类型通常是 MultiPolygon而不是简单的 Polygon因为一个朝代的首都圈、核心州郡和边境军镇往往彼此分离甚至中间隔着政区飞地。你在做面积统计时必须注意区分几何面积和记录面积尤其是在联合属性查询时一个记录可能包含多个不连续的面。用 QGIS 的「矢量—几何工具—分解多边形」可以把多部分面拆成单个面但这样会丢属性所以拆分前最好先复制一份数据。另一个常见问题是自相交和缝隙。历史边界数字化时手绘节点很容易造成相邻面之间存在细小的缝或重叠这在制图时看起来不明显但一旦你用「处理—工具箱—修复几何」去跑拓扑检查就会报出一大堆错误。我一般在加载后立刻运行一次「几何有效检查」定位自相交、重复节点、尖刺等无效几何然后用「修复几何」工具一键修复。记住修复几何会改变边界的精确度对历史研究来说微小的极值点是可接受的因为原始数据本来就是手工数字化的。3. 数据加载与预处理半小时内把历史边界跑起来3.1 批量加载多个朝代的 SHP不同朝代的行政范围通常是单独成文件的例如qin.shp、han.shp、tang.shp每个文件内部可能还有多个政权例如三国时期魏、蜀、吴三个图层。如果你拿到手的是一整个目录几十个文件批量加载才是正确打开方式。QGIS 里面可以用「图层—添加图层—添加矢量图层」然后按住 Ctrl 多选所有 SHP 文件一次性导入。加载完成后每个图层会默认用随机颜色渲染你可以在图层列表里批量选中它们右键「分组」把全部历史数据放入一个组里统一管理。如果是用代码批量加载GDAL 的ogr库可以遍历目录并读取每个文件的元数据推荐在正式进入桌面软件之前先跑一遍把每个文件的坐标系、要素数量、字段列表打印出来形成一份“数据体检报告”。这样你能在一分钟内发现哪个文件编码错了哪个文件坐标系没有定义而不需要逐个在 GUI 里点开属性看。from pathlib import Path from osgeo import ogr shapefiles list(Path(historical_shp).glob(*.shp)) for shp in shapefiles: ds ogr.Open(str(shp)) if ds: layer ds.GetLayer(0) srs layer.GetSpatialRef() print(shp.stem, layer.GetFeatureCount(), srs.ExportToWkt()[:50])这段代码使用 GDAL 的 Python 绑定遍历指定目录下的所有 SHP 文件输出文件名、要素数量和空间参考的 WKT 前 50 个字符。你可以在任何有 GDAL 的 Python 环境里运行它不需要安装桌面 GIS。如果发现某个文件的空间参考为空srs是None你就知道这个文件需要手动指定坐标系了。强制指定时要注意很多历史数据其实是用经纬度存储的只是缺少.prj文件那么你直接指定 EPSG:4326 即可而不需要复杂的投影转换。3.2 项目投影选择做分析时使用什么投影加载完所有朝代后你还需要做一件事为项目设置一个合适的工作投影。历史行政区划跨度极大从西域到东海从漠北到岭南如果直接用 WGS84 经纬度计算面积结果会严重失真因为纬度越高经纬度网格代表的实际面积扭曲越大。做面积统计或距离计算时必须选择一个适合中国大陆区域的等积投影我的习惯是用 Albers 等积圆锥投影EPSG:102025中央经线选 105°E双标准纬线选 25°N 和 47°N。不同 GIS 软件设置参数的位置不同QGIS 中是在「工程—属性—CRS」里选择或自定义。如果你只做制图展示而不做测量那么用 WGS84 经纬度也没问题因为显示效果没有太大差别。但一旦你要计算各朝代疆域面积、统计政区面积分布就必须在分析前把图层重投影到 Albers 投影下否则算出来数字会偏大或偏小得离谱。用ogr2ogr批量重投影也很方便ogr2ogr -t_srs EPSG:102025 tong_albers.shp tong_4326.shp注意EPSG:102025 是一个 ESRI 内部定义的 Albers 投影有些 GDAL 版本可能不认识你可以用 PROJ 的字符串代替。在 QGIS 里设置自定义投影时用以下参数projaea lat_125 lat_247 lat_00 lon_0105 x_00 y_00 datumWGS84 unitsm no_defs。这些参数的含义分别是等积圆锥投影、双标准纬线为北纬 25 度和 47 度、中央经线为东经 105 度、单位是米。这样重投影后面积计算就可靠了。3.3 字段清洗与时间范围裁剪历史行政区划数据中年份字段并非总是干净的。有些文件里只有朝代名没有具体起止年份有些文件里年份是文本格式比如「前221」和「-206」混用还有些会把西汉和东汉都笼统写成「汉」。在做时间序列分析之前必须统一年份字段的数据类型和格式。我用 Python 的pandas读取 DBF 文件通过pandas直接读注意需要安装pandas-dbf插件或者先用ogr2ogr把 SHP 转成 GeoJSON/CSV然后清洗start_year和end_year两列把公元前年份统一为负数。import pandas as pd # 读取属性表CSV格式假设已导出 df pd.read_csv(song_dynasty_attrs.csv, encodingutf-8) # 把字符串年份转换为整数非数字强制为NaN df[start_year] pd.to_numeric(df[start_year], errorscoerce) df[end_year] pd.to_numeric(df[end_year], errorscoerce) # 输出年份范围 print(df[start_year].min(), df[end_year].max())这段代码处理了文本年份混乱的情况。pd.to_numeric会把形如“960”的字符串转成整数同时把无法转换的值变成NaN方便你后续检查哪些记录缺年份。很多历史数据把朝代之间的过渡期也做进图层里比如隋末唐初、宋辽金并行这些时期如果你只想显示某个特定年份例如公元 1100 年需要用条件筛选start_year 1100 end_year然后导出为新的图层。这里的关键点是历史行政区划是随时间动态变化的用单一朝代名替代年份范围做筛选很容易把多个政权的疆域混杂在一张图中。4. 按朝代查询与制图把时间维度装进图层里4.1 属性筛选实现单朝代显示当你需要展示某个朝代的疆域时可以直接用 SQL 查询语句在属性表里做筛选而不需要拆分成多个文件。QGIS 的「按表达式选择要素」支持 SQL 语法例如想选中唐代所有州级边界可以写dynasty 唐 AND level 州。但前提是字段名和字段值必须与你数据集的属性表一致否则查询结果为空。为了避免字段名不一致的问题我建议在加载数据后立即用「属性表—字段计算器」新建一个规范的字段比如将dynasty统一规范化成全拼或标准简称同时去除文本中的空格。如果使用 Python 的fiona或geopandas做筛选操作更灵活也更适合批量处理。geopandas可以直接读取 SHP用布尔索引筛选用例然后写回新的 SHP 文件。例如要从全部历史数据中提取元代的所有疆域要素只需要两个条件朝代名称是「元」或者年份范围跨越 1271 到 1368。需要注意的是元代疆域包含岭北行省、辽阳行省等几个大区域而且这些区域与汉地图的比例尺差别大所以筛选结果可能需要先检查几何有效性再出图。import geopandas as gpd # 读取元代全图 all_dynasties gpd.read_file(all_historical.shp) # 条件是朝代字段为“元”并且开始年份1368且结束年份1271 yuan all_dynasties[(all_dynasties[dynasty] 元) (all_dynasties[start_year] 1368) (all_dynasties[end_year] 1271)] # 重投影到Albers后再保存 yuan.to_crs(epsg102025).to_file(yuan_albers.shp, encodingutf-8)这里to_crs(epsg102025)会把数据重投影为 Albers 等积圆锥保存时指定encodingutf-8防止再次出现中文乱码。我建议所有从中文 SHP 导出的文件都显式指定编码因为 GDAL 默认生成 DBF 时可能使用系统 ANSI 编码在 Linux 系统下容易变成未知编码。4.2 做时间轴动画多图层叠加与透明度历史行政区划最吸引人的表达方式是时间动画以五年或十年为步长动态改变显示范围。实现方式有两种一种是在 GIS 软件里手动创建多个年份切片图层另一种是用 Python 生成一批年份状态的 GeoJSON 文件再用 QGIS 的时间管理器插件循环播放。我推荐用代码生成切片因为手动复制图层再多设置可见性非常费时。使用geopandas可以按年份循环过滤现有数据每次生成一个新文件。例如以 50 年为间隔从公元前 221 年到公元 1368 年生成约 32 个时间切片。每个切片里只保留在当年存在的政区面。生成后在 QGIS 中依次加载所有切片用相同样式渲染然后使用时间管理器设置开始时间和持续时间即可。这种做法适合做「中国历史疆域变迁」视频但要注意年份跨度大时数据量也会很大建议使用 GeoPackage 格式而不是 SHP 来存储多个切片因为 SHP 文件头有 2GB 限制。4.3 符号化核心区与特殊政区的区分历史行政区的制图不是填充一种颜色就完事。不同等级的政区需要区分符号例如中央直辖郡县用实心填充羁縻州或者属国用斜线填充长城以外的游牧政权控制区用半透明小点阵来表示。在 QGIS 的样式面板中可以使用基于规则Rule-based的渲染写level 郡、regime 唐等规则。核心原则是不要用面填充色的深浅去表示时间先后因为历史数据的年份范围是区间不是瞬时值。我一般在出图时会给每个朝代图层添加白色内边框避免相邻政区边界粘连。具体做法是「符号层—添加一个简单填充—将笔划颜色设为白色—笔划宽度设为0.2 毫米」同时把外层符号层的透明度设为 20% 左右。这样能同时看到重叠区域和历史地名注记。如果你用的是 ArcGIS Pro可以用「属性—Geoprocessing 选项—导出符号」来保存样式模板以统一整组图件的风格。5. 避坑指南历史行政区划数据最常见的五个翻车现场5.1 打开后所有中文属性都是乱码现象图层加载正常但打开属性表所有汉字都显示为带菱形问号的乱码部分字符直接消失。原因SHP 的 DBF 属性表采用 GBK 编码而 GIS 软件默认按 UTF-8 读取。我在用 QGIS 3.x 时遇到这个问题的频率非常高尤其是数据是从某些平台分享打包时原始 DBF 没有将编码字头写进文件头。解决在加载图层时「数据源管理器中」找到该图层在「连接」部分把编码从UTF-8改为GBK或GB2312再点击「确定」重新加载。如果图层已经加载到工程右键图层选择「数据属性」—「编码」改成GBK后会立即刷新。注意修改后必须重新保存一次文件用ogr2ogr -lco ENCODINGUTF-8转成 UTF-8 的 SHP否则下次加载又会乱。从那以后我每次收到历史数据都会先强制跑一遍编码检测脚本再入库。5.2 图层叠加后相距 0.3 度位置明显偏离现象在 QGIS 中把明代边界与现代乡镇边界叠加明代图层整体往西北或东南偏移 0.3 到 1 个经度海岸线对不上。原因数据源使用老基准面如 1954 北京坐标系或克拉索夫斯基椭球体你只是手动把图层的投影声明改成了 WGS84但实际上几何坐标数值仍然是基于旧椭球体角度导致系统性偏移。这个偏移不是旋转或平移一条线而是每个点经纬度都有差异在中国范围大约几百米。解决在未加载前就明确原坐标系。如果你无法从数据源文档或.prj文件判断可以通过图层与河流/海岸线的相对位置估算偏移方向然后用控制点做仿射变换。在 QGIS 中用「矢量几何—重新投影图层」配合自定义 PROJ 字符串做七参数转换但最简单的方式是用ogr2ogr把源强制指定为EPSG:4214Beijing 1954 经纬度再转回EPSG:4326。如果还偏就需要打开.prj文件查看DATUM[D_Beijing_1954]之类的关键字再决定基准面参数。5.3 几何自相交导致面积统计失败现象用「字段计算器」计算多边形面积时某些要素弹出「几何无效」错误或者在相交分析时部分面缺失。原因手绘数字化时节点顺序错误、线段交叉导致多边形外环自相交。QGIS 的Fix geometries能解决大部分但有些自相交发生在多部分面之间修复后会丢失内部孔洞比如湖泊和飞地消失。解决先用「矢量—几何工具—校验几何」将所有错误打印到信息栏然后批量修复。修复后再检查要素数量没有减少如果减少则需要对原始数据进行人工检查用顶点编辑器删除多余节点。我曾经处理过一份宋代两浙路的数据一个多边形有 3000 多个节点其中十几个节点互相交叉修复后面积从错误的 12 万平方公里修正到真实的 9.8 万平方公里结果影响很大所以面积统计前一定先做几何检查和修复。5.4 两个朝代的时间范围重叠筛选结果冲突现象查询「公元 980 年」的疆域时同时选中了宋代和辽代的部分记录看起来像边界叠加。原因历史时期存在并立政权比如十国中的吴越和南唐宋建隆年间同时存在多个政权而年份筛选条件只写了start_year 980和end_year 980没有排除政权属性。另外有些数据把「朝代」字段填成统治者的庙号或年号比如「北宋」「宋」混用都被筛选出来了。解决给你的筛选条件增加regime字段的判断明确提出你需要哪个政权。在做时间切片时先确定切片年号对应的标准政体然后在iso_year字段标准化年份上做交集筛选。更好的办法是建立一份「年份—政权对照表」把每个年份对应到唯一政权集再用表连接去重。5.5 保存的 SHP 到其他软件里打不开现象在 QGIS 中正常编辑并保存的 SHP拿到 ArcGIS 或 FME 里打开时提示「无法打开矢量图层」或只能看到属性表。原因QGIS 保存 SHP 时默认在每个要素后附加UTF-8编码的.cpg文件但 ArcGIS 某些版本对.cpg支持不完整如果属性字段名包含中文或超过 10 个字符也会造成兼容问题。此外保存路径包含中文也可能导致 ArcGIS 无法解析。解决通用规则是字段名一律使用英文字母长度不超过 10。保存时选择ESRI Shapefile并在图层选项中设置ENCODINGGBK或UTF-8同时把文件路径里的中文换成英文目录。如果实在要跨软件协作最保险的方案是导出为 GeoPackage 格式.gpkg几乎所有现代 GIS 软件都能直接读取且没有 DBF 字段长度和编码问题。从我自己的经验看用.gpkg代替.shp作为历史数据中间格式能省下很多不必要的兼容性困扰。6. 进阶用法把历史政区中心点与今日地名做模糊匹配这个数据集的潜力远不止画图。把历史行政范围转为政区中心点再与今天的地名做关联可以做人口迁移、政区沿革、稻作扩散等课题。历史政区名与今天地名往往不完全一致比如「长安」对应今天的「西安」或者「江陵」对应「荆州」你需要一个模糊匹配算法来辅助关联。第一步从 SHP 中提取每个政区面的质心或中心点。用 QGIS 的「几何工具—多边形质心」即可注意质心可能落在面外比如弯月形政区建议改用Point On Surface函数它能保证点落在面内。导出 CSV 时保留政区名和朝代名两个字段。第二步使用 Python 的difflib或fuzzywuzzy做文本相似度匹配。历史地名与现代地名存在“古今分合”关系单纯相似度不够还需要手工校对。我的习惯是先把所有历史地名与现代行政区划表可以来自公开的民政数据进行归一化去除「郡」「府」「路」等通名再比较核心专名。如果相似度大于 90直接接受在 70 到 90 之间人工复核低于 70记录为未匹配。from fuzzywuzzy import fuzz # 历史地名列表与现代地名列表简化示例 hist_names [长安, 洛阳, 江陵, 会稽] modern [西安, 洛阳, 荆州, 绍兴] def match(name, candidates, threshold70): for cand in candidates: score fuzz.partial_ratio(name, cand) if score threshold: return cand, score return None, 0 for n in hist_names: matched, score match(n, modern, threshold80) print(n, -, matched, score)这段代码利用fuzzywuzzy的partial_ratio做子串匹配因为历史地名通常是现代地名的旧称包含关系较多。比如「长安」与「西安」的相似度可能不高但「长安」与「长乐」等词会有高分所以实际使用时需要结合朝代年份来精化匹配范围。更高阶的做法是使用一个历史地名词典表把长安直接映射到西安数据量不大时手工建映射比算法靠得住。第三步拿到匹配结果后可以将历史中心点与现代行政区面做空间连接计算政区面积级别的包含关系。例如把一个明代「府」的中心点落到今天的某个地级市内如果落进去就认为该府的中心区与今天该市存在承载关系。这一步用geopandas.sjoin实现不了因为点是点与面的连接但可以用overlay或空间索引加速。我的一个实际做法是给每个历史中心点添加一个 5 公里半径的缓冲区再用intersects判断缓冲区是否穿过了今天的边界这样能忽略政区边界细微变化提高匹配率。另一个值得尝试的进阶方向是地形叠加。历史行政区边界经常沿着山脊或河流将 SHP 数据与 DEM数字高程模型叠加可以分析某一朝代的政区中心分布与海拔、坡度之间的关系。在 QGIS 中用「采样栅格值」工具把 DEM 的高程值写入政区中心点属性表然后再用统计工具按朝代分组计算平均海拔。我做过一个唐和宋的对比发现唐代州治平均海拔比宋代高约 80 米这间接反映了经济重心从西北向东南转移的地形因素。这种分析需要先把边界重投影到 Albers然后使用gdalwarp裁剪 DEM 到研究范围否则数据量很大计算会很慢。最后提醒一点历史地理数据永远不可能 100% 精确因为它是对古代文献、考古地图和现代测绘的折中复原。在你利用这份数据集做研究或汇报时一定要在成果中标注原始数据的制图单位和时间分辨率不要给人「精确边界」的错觉。从那以后我每次拿到新的历史行政区划数据集都会强制先完成属性编码检查、坐标系确认、几何修复三件事再进主题分析。这套流程虽然多花 15 分钟但能拦下大部分后续返工希望帮到你。本文还有配套的精品资源点击获取