☰
地名数据库二期建设:从编码规则到数据清洗的落地指南
2026/10/12 21:25:26 网站建设 项目流程

简介:《地名数据库二期建设总体方案.doc》是一份面向民政区划地名管理信息化建设人员、GIS项目规划者与政务系统开发者的完整项目方案,围绕省市县三级地名数据库共享交换、基础地图定期更新机制和数据库结构升级展开,适合作为政务GIS类项目立项参考或方案模板。资源包内共1个doc文档,大小489KB,文档正文包含一期建设成果回顾、二期标准规范编制、基础地图数据更新、地名数据向国家3.0版本迁移、勘界成果数据建库,以及模型驱动与SOA总体技术方案等章节。目前已有43人学习下载。读者可直接借鉴其中与国土部门共享基础地图以降低更新成本的做法,也可参考旧库资源盘点、20万条存量数据迁移与10万条沿海普查数据手工录入的工作量评估,以及系统可伸缩、可扩展架构设计思路,对同类数据库二期项目具有较强的参考价值。

1. 地名数据库二期:一期烂账不还清,二期就是第三次返工

干政务 GIS 和数据库的人对「地名数据库」四个字不会陌生:查得出来,但不敢用。一期建库时数据分散在 Excel、CAD、Shp 里,坐标各说各话,地名编码对不上,历史地名改了就丢,更别提跟国土、规划部门互相读数据。这份《地名数据库二期建设总体方案》解决的正是「一期烂账怎么在二期一次性还清」:统一编码规则、统一坐标体系、统一质量检查、统一增量更新机制。读完你能直接拿走一套可落地的表结构、清洗脚本的关键逻辑,以及我在类似项目里踩过的坑。适合正在升级地名库、地址库、POI 库的从业者,也适合准备从零建库但不想第三次返工的人。

2. 总体方案解读:二期到底比一期多做了什么

二期不是把库扩容,也不是把界面做漂亮。方案层面首先要回答一个问题:一期留下哪些不能继续用的东西,二期用什么样的结构去承接。这一章先把方案拆开看,讲清楚框架和选型理由。

2.1 一期遗留问题的清单化梳理

我在方案评审会上最常做的一件事,就是把一期数据的问题列成一张表,让承建方挨个确认。因为「统一建设」四个字不落到具体问题上,二期就会变成一期的复制品。下面这张表是地名库项目里最典型的一期遗留问题,方案里对应的处理策略也一并列出。

一期典型问题具体表现二期方案对策
地名编码不统一同一个村在台账、图纸、系统里编码不同建立 12 位统一地名编码,旧编码进 alias_code 字段
坐标体系混乱有的用 WGS84,有的用 GCJ02,老图是北京54全部转 CGCS2000,原始坐标保留备查
历史地名丢失地名撤销后记录被物理删除,查不到来龙去脉历史地名单独建层,主表逻辑删除
增量更新靠人工新增、消亡地名靠手工改,没有审计建立增量变更表,时间戳驱动同步
字段口径不一致「别名」有的写简称,有的写旧名字段字典化,别名、简称、旧名分开

做这一步的时候,不要只看抽样数据。我一般会导出一期全量数据跑一遍校验脚本,把 null、重复、超长、乱码的记录全部列出来,按问题类型分组。方案文档里这部分可能只写了「全面梳理存量数据」,但真正落地时,这一张清单决定了二期建库的工作量和工期。

2.2 总体框架:三库一平台,还是四层架构

二期方案的总体框架,常见做法是「三库一平台」:基础地名库、历史地名库、专题地名库,外加一个地名管理与服务平台。基础库承载现行标准地名,历史库承载撤销、更名的地名记录,专题库承载界线类、文化保护类等具有专题属性的数据。

平台层可以拆成四个逻辑层:数据层负责存储和读写,服务层提供地名查询、匹配、校验接口,应用层面向民政、规划、交通等业务系统,管理层负责用户权限、日志和配置。方案文档里的架构图通常画得很复杂,但实际动手时最重要的是把「一个地名在三个库里怎么流转」这个流程想清楚。基础库的地名走撤销流程时,不是删除而是迁移到历史库,同时主表打上 is_deleted 标记;这个流程定了,后面的同步、匹配、审计才有的可做。

提示:如果方案里出现「一台服务器一个库」的做法,要警惕。地名库的数据量本身不大,但跨部门调用、跨库关联是常态,物理拆得太散,联查和同步都会很痛苦。

2.3 数据库选型:PostgreSQL 为主,国产库做备选

地名数据本质上是带空间属性的数据,选型时要考虑空间索引、坐标系转换、中文全文检索这三个刚需。我一般推荐 PostgreSQL 加 PostGIS 作为主选:查询能力强,空间函数齐全,中文和地理信息生态成熟。方案里如果写的是 MySQL,要慎重,MySQL 的空间数据类型和空间函数比 PostGIS 弱不少,复杂的地名拓扑关系查起来很别扭。

如果项目对国产化有要求,达梦和人大金仓是常见的替代。达梦兼容 Oracle 语法,金仓基于 PostgreSQL 内核,PostGIS 的很多函数在上面能直接跑或者小改后跑。金仓用 Docker 部署时要注意镜像里是否自带 PostGIS 扩展,有的镜像只装了内核不带空间扩展,装上之后 ST_GeomFromText 会直接报函数不存在。数据库同步软件方面,如果是 PG 生态,用逻辑复制做增量同步最稳;跨库同步就要依赖 DataX、Kettle 这类工具,方案里需要明确同步的粒度是按表还是按天全量。

选型这一节,建议方案里多写一句「坐标系统一采用 CGCS2000,EPSG 代码 4490」。这句话看着简单,但能把后面所有空间数据处理的坐标系问题一次堵死。用 MySQL 的人经常忽略坐标系这个问题,直接拿 WGS84 的经纬度存进去,出图时叠加别的数据源才发现偏移。

3. 地名编码与数据标准:建库前先把字段和码表钉死

地名库最核心的不是数据库性能,而是数据标准。字段没定死、码表没定死,后面每一轮数据接入都会吵一架。这一章把编码、字段、分类码表三个东西讲透,这些也是方案文档里应该能找到、但常常被讲得一笔带过的地方。

3.1 地名唯一标识:从 9 位演进到 12 位编码

一期常见的是 9 位编码,结构是「6 位行政区划码 + 3 位序号」。问题在于:撤销的地名序号不能复用,新增地名多了之后序号不够;跨省协作时,两个省各自的序号段没有统一管理,撞号时有发生。二期的常见做法是升级到 12 位,结构分成四段:

  • 第 1-6 位:县级行政区划代码,例如 110105 代表北京市朝阳区
  • 第 7-8 位:地名类别码,对应分类码表中的大类
  • 第 9-10 位:乡镇级代码,没有乡镇层级的用 00 填充
  • 第 11-12 位:流水号,从 01 开始顺序编码

这套编码的好处是能从编码本身读出处所层级和类别,做权限控制和数据分片时都方便。坏处是地名撤销后流水号不能复用,否则历史记录会串。方案里如果还写了 alias_code 字段,就把旧的 9 位编码统一放进去,这样一期系统的历史调用还能对上号。

3.2 属性字段全集:核心字段、扩展字段与拼音字段

字段设计是方案文档里可以直接抄作业的部分。下面这张表是我在类似地名库项目里整理过的核心字段结构,二期方案里的大多数字段跟这个清单是一致的。

字段名类型是否必填说明
place_idVARCHAR(32)是地名唯一标识,12 位编码加 2 位版本位
place_nameVARCHAR(200)是标准地名全称
alias_nameVARCHAR(500)否别名、曾用名,逗号分隔
short_nameVARCHAR(100)否简称
pinyinVARCHAR(300)是全拼,小写,无分隔符
category_codeVARCHAR(4)是分类码表代码
category_nameVARCHAR(100)是分类名称,冗余存储便于查询
lonNUMERIC(10, 7)是经度,CGCS2000 坐标系
latNUMERIC(9, 7)是纬度,CGCS2000 坐标系
source_coordVARCHAR(20)是原始坐标系来源,备查
source_fileVARCHAR(300)否数据来源文件或台账
valid_fromDATE是生效日期
valid_toDATE否失效日期,历史地名时填写
is_deletedSMALLINT是0 正常,1 逻辑删除
imported_atTIMESTAMP是入库时间

拼音字段要特别说明。很多一期数据没有拼音,或者用全角输入法打出了大写拼音,后期维护成本极高。二期建库时拼音建议用程序批量生成,生成后人工抽检多音字。地名里的多音字是重灾区,比如「重庆」的「重」读 chong 不读 zhong,程序默认按常用字处理,但「重庆」这类专名必须维护一个多音字词表。

3.3 分类码表:从大类到小类的粒度控制

地名分类码表是整个库的灵魂。方案文档里一般会引用国标分类框架,但落到建库时要根据业务需求裁剪粒度。常见的大类划分是:政区类、群众自治组织类、居民点类、交通运输类、水利电力类、纪念地旅游类、建筑物类、自然地理实体类、历史地名类、界线类。

大类是 2 位码,小类是 2 位码,例如「0501」表示交通运输大类下的道路小类。粒度控制的原则是:能覆盖业务需求的最小粒度。不是越细越好,因为地名数据不像业务流水数据那样可以自动补全,分类太细会导致基层采集人员不会填,最终填出一堆「其他」。实际操作中,我会在方案里单独附一张分类码表,每一条码都写清楚名称、说明、示例地名,避免同一个地名在不同批次里被分到不同类别。

4. 建库与入库落地:从原始资料到可查询的数据库

有了数据标准,接下来就是建库、清洗、入库、质检这四步。这一章给出可以直接改来用的建表脚本、清洗脚本和质检 SQL,并说明每一步的参数怎么调。

4.1 标准建表脚本:PostgreSQL 加 PostGIS 的落地写法

建库的第一步是建一个带 PostGIS 扩展的数据库。下面这段脚本是基础地名表的建表骨架,主键用 place_id,几何字段用 PostGIS 的 Geometry 类型,并且指定了坐标系 EPSG 4490。

-- 启用空间扩展 CREATE EXTENSION IF NOT EXISTS postgis; -- 基础地名表 CREATE TABLE base_place ( place_id VARCHAR(32) PRIMARY KEY, -- 地名编码 + 版本位 place_name VARCHAR(200) NOT NULL, -- 标准地名全称 alias_name VARCHAR(500), -- 别名,逗号分隔 short_name VARCHAR(100), -- 简称 pinyin VARCHAR(300), -- 全拼,程序生成 category_code VARCHAR(4) NOT NULL, -- 分类码表外键 category_name VARCHAR(100), -- 分类名称冗余 lon NUMERIC(10, 7), -- 经度 lat NUMERIC(9, 7), -- 纬度 geom GEOMETRY(Point, 4490), -- 空间字段,CGCS2000 source_coord VARCHAR(20) DEFAULT 'CGCS2000', -- 原始坐标系记录 valid_from DATE NOT NULL, -- 生效日期 valid_to DATE, -- 失效日期 is_deleted SMALLINT DEFAULT 0, -- 逻辑删除标记 imported_at TIMESTAMP DEFAULT now() -- 入库时间 ); -- 空间索引,地名按点位查询是高频操作 CREATE INDEX idx_base_place_geom ON base_place USING GIST (geom); -- 名称索引,支持模糊查询 CREATE INDEX idx_base_place_name ON base_place (place_name);

逻辑说明:geometry 字段直接建在表上,而不是单独建视图再关联,是为了让空间查询走索引;索引用 GIST 而不是 B-tree,因为空间数据是树形格网结构,B-tree 在点查询上勉强能用,范围查询和缓冲区分析性能会明显变差。两行注释不能少:source_coord 字段在很多人看来是冗余的,但它是排查坐标偏移的第一现场。

参数说明:NUMERIC(10, 7) 和 NUMERIC(9, 7) 的精度对应经纬度小数点后 7 位,约等于 1.1 厘米的精度,对地名点位足够;如果方案里有更精确的测量成果,可以把经纬度直接提升到 NUMERIC(12, 8),但空间字段的类型不用改。EPSG 4490 一定要写对,写 4326 是 WGS84,出图时叠加 CGCS2000 的数据就会偏。

4.2 清洗脚本:坐标归一化、拼音补全、必填项校验

一期数据最常见的形态是 Excel 台账,里面字段乱、缺失多、坐标和名称混在一起。下面这段 Python 脚本截取了清洗流程里最关键的三个逻辑:拆坐标、补拼音、过滤必填项缺失的记录。

# 清洗一期地名台账:坐标拆分、拼音补全、必填项过滤 import pandas as pd from pypinyin import lazy_pinyin df = pd.read_excel("stage1_places.xlsx", dtype=str) # 必填字段清单,缺失或为空则剔除,但单独记录原因 required = ["place_name", "category_code"] invalid = df[df["place_name"].isna() | (df["place_name"] == "") | df["category_code"].isna() | (df["category_code"] == "")] invalid.to_excel("invalid_records.xlsx", index=False) df = df.dropna(subset=["place_name", "category_code"]) # 一期常见问题:一列存了"东经116.123,北纬39.456" def split_coord(raw): if not isinstance(raw, str): return None, None cleaned = raw.replace("东经", "").replace("北纬", "") cleaned = cleaned.replace(",", ",").replace(" ", ",") parts = [p for p in cleaned.split(",") if p] if len(parts) < 2: return None, None try: return float(parts[0]), float(parts[1]) except ValueError: return None, None df["lon"], df["lat"] = zip(*df["coord_str"].apply(split_coord)) # 拼音补全,多音字词表单独维护 def name_pinyin(name): return "".join(lazy_pinyin(name)).replace(" ", "").lower() df["pinyin"] = df["place_name"].apply(name_pinyin) # 输出清洗结果 df.to_csv("cleaned_places.csv", index=False, encoding="utf-8-sig")

逻辑说明:脚本先做必填项过滤,这不是删数据,而是把不合格记录单独导出,给人工复核留依据;坐标拆分处理了「东经、北纬」这种中文描述混在一列的情况,清洗后重新生成经纬度;拼音用 pypinyin 库生成全拼,多音字问题靠词表修正。

参数说明:encoding="utf-8-sig"这个参数容易被忽略,地名台账最终要给政务系统用,很多老系统按 GBK 读取 CSV,不加 BOM 头会在导入时乱码;dtype=str强制所有字段按文本读,避免 pandas 把编码为纯数字的地名 ID 自动转成 int64 造成精度丢失。清洗后的 CSV 不要直接入库,先人工抽检 200 条,重点看坐标拆分的准确率和多音字处理的正确性。

4.3 质检 SQL:一跑就知道库建得干不干净

入库完成后不能直接上线,要跑一轮质量检查。下面三句话是地名库质检里最常用的,分别检查几何字段是否为空、是否存在同名不同编码、是否存在编码前缀与分类码不一致。

-- 1. 几何为空的地名,这类记录无法上图 SELECT COUNT(*) AS empty_geom FROM base_place WHERE geom IS NULL; -- 2. 同名但编码不同,人工判断是否为重名或历史沿革问题 SELECT place_name, COUNT(*) AS cnt FROM base_place WHERE is_deleted = 0 GROUP BY place_name HAVING COUNT(*) > 1; -- 3. 编码第 7-8 位与分类码不一致的记录 SELECT place_id, place_name, category_code FROM base_place WHERE substr(place_id, 7, 2) != category_code;

参数说明:第三条 SQL 利用了 12 位编码的设计——第 7-8 位是类别码。如果方案里没采用这种编码结构,这条语句要改成关联分类码表校验。质检结果不是看到问题就改,而是把问题记录导出成工单,分给数据责任人确认。这个环节别省,我在项目里见过太多「建库一天、返工一周」的案例,根因就是质检这步被压缩了。

5. 避坑:地名库二期建设的五个翻车现场

地名库项目里有一类坑是方案文档里不会写、但现场一定会遇到的。我挑了五个最常见的,按「现象 → 原因 → 解决」的套路写出来,每一个都是从真实项目里血泪换来的。

5.1 坐标偏移:地图上叠加后点位差了几百米

现象:地名点位在自己的系统里看着正常,但叠加高德或别的部门数据时,点位整体偏移几百米。原因:一期数据混用了 WGS84、GCJ02 和北京54 三种坐标系,坐标入库时没有统一转换。GCJ02 是国内常用对外的加密坐标系,WGS84 的 GPS 原值放进地图里就会偏移。解决:入库前先把所有来源数据的坐标系标注清楚,再统一用 PostGIS 的ST_Transform转到 EPSG 4490;如果原数据是 GCJ02,要先用专门的加密坐标转换算法转回 WGS84,再用ST_Transform转 CGCS2000。

5.2 并发导入死锁:多线程灌数据只有一台机器在跑

现象:用多线程同时往base_place表插数据,跑着跑着数据库报deadlock detected,事务回滚,导入中断。原因:多个事务按不同顺序更新同一批地名记录,锁等待形成环;数据库死锁检测一触发,就把其中一个事务当作牺牲品回滚掉。解决:导入时先对place_id排序,每个线程只处理一段排序后的数据,减少锁冲突;同时给会话设置lock_timeout = 5s,避免一个线程卡住整个导入流程。另一个常见做法是把大事务拆成小批量,每 500 条提交一次,这样死锁影响范围可控,重放成本低。

5.3 增量同步黑洞:撤销的地名在下游还在展示

现象:二期系统里撤销了某个自然村,但地名应用平台、地图服务里依然能查到。原因:一期的数据同步机制是每天全量快照,或者增量脚本只同步 insert 和 update 而没有处理 delete;更隐蔽的是同步逻辑里把「物理删除」当成了「没有变化」。解决:主表不物理删除,一律用is_deleted和valid_to做逻辑删除;同步工具每次读取imported_at大于上次同步时间的记录,并将is_deleted = 1的记录同时推送给下游。

5.4 通名与专名不拆分:搜「村」一个字都出不来

现象:查「王村」能搜到,查「村」却什么都搜不到。原因:地名字段里存的是「王村」这样的全称,通名「村」被当作专名的一部分存进了同一个字段;全文检索时,检索词「村」没有匹配到完整名称。解决:字段设计里把专名和通名拆开:专名表示独有的部分,通名表示类别属性;查询时先命中地名类型,再在专名字段上做模糊检索。配合 PostgreSQL 的 GIN 索引和to_tsvector中文分词,像「镇」「街道」「社区」这类通名也能正常检索到。

5.5 历史地名没有版本:一改沿革,来龙去脉全断

现象:某个地名的沿革写错了,人工修改后原有沿革信息找不回来了,审计时说不清楚改动记录。原因:方案里没有设计历史版本表,update 直接把原值覆盖。解决:历史地名和沿革变更单独建表,每次 update 之前把旧记录写入place_version_log,字段包括place_id、old_value、new_value、change_time、operator。这块设计要花时间,但没有版本记录的库,第二年回顾数据时就是个黑匣子。

6. 一期 Excel 台账的清洗救火:一个可复用的收尾流程

方案验收前最常遇到的情况是:标准建好了,库也建好了,但一期那批最脏的 Excel 台账还没处理完。这里给一个我实际用过的收尾流程,专门处理那些「看起来能入库、一验证全是坑」的遗留数据。

流程分四步:第一步,把一期所有台账文件集中到一个目录,文件名统一改成「区域_数据批次.xlsx」;第二步,用清洗脚本跑一遍,脚本的关键参数是必填字段清单和坐标列名映射,这两个参数在第一轮跑完就要根据报错调整;第三步,把清洗结果按街道、乡镇拆分导出,交给对应区域的数据确认人核对;第四步,核对通过的数据走批量导入,导入后执行第 4.3 节的三条质检 SQL。

这个流程的关键在于第二步的参数调整。比如有的台账把坐标写成了「116°23'45"」这种度分秒格式,第一次跑坐标拆分脚本全部失败;我一般在代码里加一个正则分支,先匹配纯数字格式,再匹配度分秒格式,两种都匹配不上才判为缺失。多音字词表也一样,第一轮只维护了「重庆」这类高频词,跑完抽检发现「堡」字在不同地名里读法不同,词表又补了一轮。

从那以后,我经手任何地名库项目,都在建表脚本里强制写上source_coord、source_file、imported_at这三列,并在需求评审时要求对方确认「一期数据是否需要保留原始坐标记录」,而不是直接说「统一转 CGCS2000 就行」。这个习惯救过我很多次:半年后有人拿着原始台账来质疑坐标精度时,我能直接查库里的记录还原当时的数据来源,而不是靠回忆。这份方案文档里的数据标准、表结构和流程设计,如果你打算落地成自己的项目,建议先把这三列加上,再开始建表。希望帮到你。

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

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

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

立即咨询