城市用地分类标准解析:从.doc到数据字典的GIS与控规校验指南

发布时间:2026/9/18 12:40:48
城市用地分类标准解析:从.doc到数据字典的GIS与控规校验指南 简介新版城市用地分类及规划建设用地标准是城乡规划与用地管理的基础性文件适用于城市和县人民政府所在地镇的总体规划与控制性详细规划编制、用地统计和用地管理工作。这份doc文档为单文件资源约211KB集中呈现标准条文涵盖城乡用地分类、城市建设用地分类、人口规模统计、人均用地指标及城市建设用地规模与指标等核心内容。读者可据此快速查阅居住用地、公共管理与公共服务用地、商业服务业设施用地、工业用地、物流仓储用地、交通设施用地、公用设施用地、绿地等八类用地的定义与归类方法并理解人均居住用地、人均公共管理与公共服务用地、人均交通设施用地等指标的计算口径。文档适合城市规划编制人员、用地管理工作者及城乡规划专业学生作为查询和研读参考现有79人学习使用便于方案编制、备案审查与教学参考。1. 一份 .doc 里的新版标准凭什么让 GIS 表、控规和数据库都围着它转拿到“新版城市用地分类及规划建设用地标准.doc”很多人习惯把它归档就完事。但真正做规划信息化的人清楚这份文档里的每一个分类代码和比例区间都会变成用地数据库里的字段约束、GIS 图层的属性域、控规说明书的平衡表公式。代码差一位面积统计就串到别的类别比例记错一格审批系统就敢把不符合强制性要求的方案放进去。无论是自然资源和规划局的数据库管理员还是做用地数据分析的工程师都得先把这套分类和指标拆开揉碎。这篇就顺着“读文档→抽代码→建映射→写校验”的顺序把它做成一份能直接跑的数据字典。2. 先从新版城市用地分类与规划建设用地标准里把代码分层和指标口径这两条主线揪出来2.1 分类代码的“大类-中类-小类”三级结构决定字段长度和层级归属新版城市用地分类标准通常按“大类、中类、小类”三个层次组织大类用一个英文大写字母表示中类在大写字母后加一位数字小类再加一位数字。以城市建设用地为例常见的大类有R 居住用地、A 公共管理与公共服务用地、B 商业服务业设施用地、M 工业用地、W 物流仓储用地、S 道路与交通设施用地、U 公用设施用地、G 绿地与广场用地。注意这份标准还会出现 H 和 E 开头的大类前者多用于城乡用地分类里的建设用地后者是非建设用地搞混了会把水域和林地都算进建设用地。层级代码示例名称示例存储建议大类R居住用地varchar(3)level1中类R2二类居住用地varchar(3)level2小类R21住宅用地varchar(3)level3在把标准落成数据表时我一般会把代码统一存成字符串而不是数字。原因很简单R2 不是 2B11 也不是 11GIS 属性表里一旦用数值型字段前导字母就没了。中类和小类代码统一用三位字符串存大类代码可以靠长度区分或者单独加一个 level 字段1 表示大类、2 表示中类、3 表示小类。判断层级不一定非要靠 code 长度因为大类 R 和 R1 长度不同但最稳妥的方式还是从标准文档的标题缩进里判断。拿文档里的段落举例我常用的识别脚本长这样import re lines [ R 居住用地, R1 一类居住用地, R11 住宅用地, R12 服务设施用地, ] for line in lines: # 先去掉前导空格和全角空格 text line.strip().replace(\u3000, ) m re.match(r^([A-Z])(\d{0,2})\s(\S.*)$, text) if m: letter, digits, name m.groups() code letter digits level 1 len(digits) print(code, level, name)这里正则把“一个字母开头、后面跟着零到两个数字、再跟名称”的文字抽出来。level 的计算规则是没有任何数字就是大类一个数字是中类两个数字是小类。对于从 Word 里复制出来的段落这个正则能覆盖大多数分类条目。但你要留心文档里还有“表 2.1 城市建设用地分类和代码”这样的表格表格内容用 paragraph 拿不到后面第三章会专门处理。在实际建模时我还会给代码表加一个 is_valid 标志用于区分标准表里的正式代码和附录里的“规划可参考代码”。这个标志能帮助后续校验时忽略一些实验性代码避免把还不是正式标准的代码写进审批规则。2.2 规划建设用地标准里的指标不是参考值而是强制校验阈值文档里除了分类代码另一块硬内容是规划建设用地标准。常见做法是把“人均建设用地面积”“住宅用地占城市建设用地的比例”“绿地与广场用地比例”等做成阈值表。新版标准对城市建设用地的结构有明确区间要求不同城市规模可能对应不同档次例如“规划人均城市建设用地面积”会被分成多个档次每个档次还分现状和规划两个口径。指标名称代码口径是否强制性备注居住用地比例R 类之和是新版标准按城市规模给区间公共管理与公共服务用地比例A 类之和是与旧版 C 类对照看工业用地比例M 类之和是大城市与中小城市区间可能不同道路与交通设施用地比例S 类之和是不含铁路/公路区域设施绿地与广场用地比例G 类之和是应扣除生产绿地注意我不在表格里写死具体数是因为新版标准在不同省市的实施细则里会有微调而且规划报批阶段的“强制性内容”以最新发文为准。你在落地规则引擎时最好把阈值单独存一张 indicator_standard 表不要写死在存储过程里。否则标准更新时你得去翻每一个 CHECK 约束。指标口径里的“现状”和“规划”两套数也经常被混用。现状建设用地面积来自现状调查规划建设用地面积来自用地布局方案。校验方案时只能用规划口径拿现状比例套规划标准会误报。这一章读文档时的任务是把代码层级和阈值口径先确立下来。代码层级的直接产出是一个 land_use_code 清单指标口径的产出是一个 indicator_standard 阈值表。后边的所有转换和校验都以这两份结构化数据为基准。3. 把新版城市用地分类标准 .doc 转成 CSV/SQL解析步骤和避坑点3.1 先用 LibreOffice 把 .doc 批量转 .docx避免在服务器上依赖 Office拿到手的文档是 .doc 老格式Python 的 python-docx 不支持win32com 又只能在 Windows 且装了 Word 的机器上用。我一般在 Linux 服务器上走 LibreOffice headless 模式先把 .doc 批量转成 .docx再用 python-docx 解析。转换命令是这样soffice --headless --convert-to docx --outdir ./converted /data/inputs/新版城市用地分类及规划建设用地标准.doc参数说明--headless让 LibreOffice 不弹界面--convert-to docx指定输出格式--outdir指定输出目录最后一个参数是源文件路径。转出来的 docx 会和原文件放在同一个目录文件名相同后缀改成 .docx。注意转换前要保证运行 LibreOffice 的系统用户有权限读写源目录否则 soffice 会静默失败。加一个文件存在性检查会更稳。if [ -s ./converted/新版城市用地分类及规划建设用地标准.docx ]; then echo converted; else echo failed; fi如果服务器没有 LibreOffice也可以先在本机 Word 里另存为 docx 再上传但批量定时更新时用命令行最省事。文档里带大量图片和表格时转换耗时会长一些但单文档一般几十秒内能完成。3.2 用 python-docx 同时读取段落和表格别漏了表里的分类代码转成 docx 之后我的解析策略是分三条线第一条读 paragraphs 拿正文段落里的分类代码第二条读 document.tables 拿“分类和代码”表里的小类第三条读页眉页脚把文档标题和发布机构留作元数据。python-docx 里 tables 不是 paragraphs 的一部分所以常见踩坑是只遍历了 paragraph 就以为拿全了代码遇到表格里的小类全没有。import docx, re doc docx.Document(converted/新版城市用地分类及规划建设用地标准.docx) records [] # 从正文段落抽取 for para in doc.paragraphs: text para.text.strip().replace(\u3000, ) m re.match(r^([A-Z]\d{0,2})\s(.)$, text) if m: records.append((m.group(1), m.group(2).strip(), paragraph)) # 从所有表格抽取第一列往往是代码第二列是名称 for table in doc.tables: for row in table.rows: cells [cell.text.strip() for cell in row.cells] if len(cells) 2: code cells[0] name cells[1] if re.match(r^[A-Z]\d{0,2}$, code) and name: records.append((code, name, table))这段代码里正文正则用^([A-Z]\d{0,2})\s(.)$可以同时匹配 R、R1、R21 这类代码。再看表格row.cells会把每一列都取出来通常第一列是代码第二列是名称。但很多标准表格的表头在第一行比如“代码 名称 说明”所以循环里要先判断 code 是不是“代码”这个词是就直接跳过。另外合并单元格会导致 cells 重复需要自己用dict.fromkeys(cells)去重后再判断长度。抽取出来的 records 只是内存列表下一步写入 PostgreSQLCREATE TABLE land_use_code ( code varchar(3) PRIMARY KEY, name varchar(100) NOT NULL, level smallint NOT NULL, source text );level 按上一章规则从代码长度推导source 用来区分“正文段落”和“表格”。这样以后追溯某条代码是来自正文还是表格不必重新翻文档。3.3 处理“说明”和“注”里藏着的例外条款别让注释变成脏数据标准文档里常见“注面积小于……的用地可并入……”或“混合用地按主导功能归类”这类说明。它们不是分类代码但会影响代码归类逻辑。在生成 SQL 时我会把注释单独放到 land_use_note 表与代码表通过 code 关联。比如CREATE TABLE land_use_note ( id serial PRIMARY KEY, code varchar(3) REFERENCES land_use_code(code), note text );解析时如果遇到以“注”“备注”开头的段落先拿出来不要直接丢掉。实际问题里很多规划人员就是看了正文忽略表格下面的脚注导致停车场用地被错分到“交通场站用地”还是“社会停车场用地”之间来回摇摆。新版标准里的注释往往能解决这种边界问题。注意标准文档里的“注”内容特别是“混合用地按主导功能归类”“面积小于5000平方米的……可并入……”这类表述必须保留到 land_use_note 表后续做自动归类时优先读它。4. 用地分类标准落到 GIS 和控规审核时的三个实操点映射、闭合、比例校验4.1 新旧代码映射表迁移旧数据前先做一次代码体检规划信息化最头疼的是历史数据。旧标准里的“C2 商业金融业用地”在新版里可能被拆成 B11、B21 等旧标准里的“M3 三类工业用地”也可能因为环保要求被重新归类。直接改原数据会污染历史分析常见做法是新建一张 code_mapping 表只存转换关系不覆盖原表。旧代码旧名称新代码新名称映射类型C2商业金融业用地B11零售商业用地拆分C3文化娱乐用地A2文化设施用地改名G12街头绿地G2防护绿地合并注意映射类型分成“一对一改名”“一对多拆分”“多对一合并”“无法自动映射”四种。前三种可以直接用 SQL 更新最后一种要人工审核。你在执行 UPDATE 之前先统计每种类型的记录数防止把“无法映射”的记录一股脑更新成 NULL。UPDATE land_parcel p SET land_code cm.new_code, land_code_note cm.mapping_type FROM code_mapping cm WHERE p.land_code cm.old_code AND cm.mapping_type IN (改名, 拆分, 合并);这段 SQL 的核心是 FROM 子句关联映射表只更新可自动映射的记录。执行前先做备份表CREATE TABLE land_parcel_bak AS SELECT * FROM land_parcel;。注意涉及拆分的旧代码面积也要按地块边界重新切分不能直接把面积原样挂到新代码上否则汇总时会和 GIS 地块边界对不上。映射表必须保留旧代码不能直接把 land_code 更新后丢失旧值否则以后做历史回溯没法查。常见做法是加一个 valid_from/valid_to 版本区间这样同一个地块在不同年份能对应不同的标准版本。4.2 用地平衡表的闭合校验和比例区间校验一条 SQL 就能做粗查控规图则里一般会有一张“城市建设用地平衡表”里面列出各类用地面积和占比。新版标准对这些占比有强制性区间所以我要做两道校验第一道所有小类面积之和必须等于建设用地总面积第二道按大类汇总的面积占比必须在阈值区间里。WITH parcel_area AS ( SELECT left(land_code, 1) AS category, sum(area) AS area FROM land_parcel WHERE land_code ~ ^[A-Z][0-9]*$ GROUP BY left(land_code, 1) ), total AS ( SELECT sum(area) AS total_area FROM parcel_area ) SELECT category, area, round(area * 100.0 / total_area, 2) AS ratio_percent FROM parcel_area, total ORDER BY category;这里left(land_code, 1)把 R21 截成 R再用category关联指标表。area * 100.0 / total_area算出占比round 保留两位。如果 total_area 为 0SQL 会除零所以在执行前要先查非建设用地记录。常见坑是有些 GIS 图层的面积字段单位是平方米有些是公顷比例校验之前必须统一成同一个单位。4.3 把“绿地与广场用地”这类组合大类单独校验有些标准类别不是单一代码比如“绿地与广场用地”在代码体系里是 G 大类但要拆开看“公园绿地”“防护绿地”“广场用地”三个小类的比例。我在做校验函数时会先把代码字典和指标字典加载成内存 map再为组合指标写专门的聚合逻辑这个比在 SQL 里处理更直观。indicator {G: (10, 15), R: (25, 40)} area_by_category {} for code, area in parcels: category code[0] area_by_category[category] area_by_category.get(category, 0) area for cat, (low, high) in indicator.items(): ratio area_by_category.get(cat, 0) / total_area * 100 print(f{cat}: {ratio:.2f}% - {PASS if low ratio high else FAIL})这个片段的核心是把“标准文档里的阈值”变成 Python dict然后拿实际面积占比去比。参数 low/high 就来自第二章建好的 indicator_standard 表。一旦标准更新只改表里的数据不改这段代码。注意这里的 category 是code[0]如果 land_code 里有小写字母或者带空格需要先统一code.strip().upper()。5. 收尾技巧把新版城市用地分类标准 .doc 生成一份 JSON 数据字典让前后端共用你如果只是自己分析导出 CSV 就够了。但一旦做信息系统前端下拉框、后端校验、GIS 符号化需要同一份代码表我一般会把刚才的 land_use_code 表 dump 成一个 JSON 文件放到后端服务的静态配置目录前端通过接口拉取后端直接用同一个文件做校验。流程是import json, psycopg2 conn psycopg2.connect(dbnameplanning usergis) cur conn.cursor() cur.execute(SELECT code, name, level, parent_code FROM land_use_code ORDER BY code) rows cur.fetchall() data [{code: r[0], name: r[1], level: r[2], parent: r[3]} for r in rows] with open(land_use_code.json, w, encodingutf-8) as f: json.dump(data, f, ensure_asciiFalse, indent2)这个 JSON 同时服务于三个场景前端表单里的“用地代码”下拉选项由它生成后端接口在接收用地面积时用校验代码是否存在ArcGIS/QGIS 的符号化也可以根据 JSON 生成 style 文件里的字段值。关键是不要在后端重新手写一份代码表那样会出“前端是新标准、后端是旧标准”的分裂。验证 JSON 是否完整我常用一个极简脚本检查所有小类代码的父级中类和大类是否都存在以及是否存在孤儿代码。codes {d[code]: d for d in data} for code, d in codes.items(): if d[level] 3 and d[parent] not in codes: print(orphan:, code, d[name])这里 parent 字段在导入时根据代码前缀生成。比如 R21 的 parent 是 R2R2 的 parent 是 R。生成 parent 的规则是parent code[:-1] if len(code) 1 else None。注意小类代码可能存在“直接挂在大类下”的特殊情况标准里说明为“其他用地”时parent 就是大类不能生硬地按长度截断。所以跑完上面这段验证脚本看到孤儿代码先查源文档不要改脚本掩盖问题。数据字典和后端校验函数共用这一份 JSON后续标准修订时只需要重新跑一遍“doc → CSV → JSON”所有依赖方自动跟着变。本文还有配套的精品资源点击获取