ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

从shp到WKT:建筑下垫面数据在SWMM建模中的高效处理与避坑指南

从shp到WKT:建筑下垫面数据在SWMM建模中的高效处理与避坑指南 简介上海2020年建筑面数据以shp格式组织覆盖城市与农村区域包含建筑轮廓、面积及人口属性适用于内涝模拟SWMM、城市规划、建筑能源建模等场景。压缩包内共6个文件含主数据文件shp、属性表dbf、空间索引shx、坐标投影prj、字符编码cpg及元数据xml体积约108.78MB数据密度可通过文件大小直观判断。目前已有621人学习下载适合需借助高精度建筑轮廓开展下垫面分析的研究者与工程师。基于建筑轮廓可计算屋顶面积、评估不透水面分布结合人口字段还可辅助城乡发展趋势研判满足城市治理与可持续发展研究的底层空间数据需求。该数据可直接导入常见GIS平台作为SWMM建模、内涝风险评估与建筑能耗分析的输入底图也可用于城乡差异对比与人口密度分布研究。1. 下垫面数据SWMM建模一张上海建筑shp省掉三天画图时间接到上海某片区内涝改造的前期评估任务时最耗时间的不是管网汇流计算而是下垫面底图。子汇水区要拆出来建筑轮廓得自己描按遥感影像一笔一笔画整整一个下午只画完三条街道。后来换了一版 2020 年上海城市农村建筑物面 shp 格式数据建筑面要素、面积、人口信息全部躺在同一张属性表里城市建筑和农村建筑都覆盖直接作为 SWMM 下垫面底图做空间连接几分钟就能按子汇水区聚合出各类下垫面的面积占比。这份数据适合排水建模、洪涝评估、海绵城市下垫面统计也适合拿到 shp 格式矢量数据后直接用代码导出为 WKT 做二次开发的从业者。后面写的每一步都是我当时在这份数据上跑通的流程。2. 拆包前的坐标系与字段检查面积、人口、编码三道关口2.1 文件结构一个 shp 是一组文件少一个都不行很多第一次碰 shp 格式的人会以为它就是一个后缀为 .shp 的单个文件双击能打开复制走一个就完事。实际上一份完整的 shapefile 是一组文件的集合常见组成如下后缀存储内容缺失或损坏的后果.shp要素的几何坐标图形完全打不开.shx几何位置索引软件无法读取几何被当成空文件.dbf属性表面积、人口字段都在这里属性全丢只剩一堆多边形.prj坐标系描述文本软件默认按 WGS84 经纬度理解位置偏移、面积算错.cpg字符编码声明中文属性乱码解压压缩包后第一步先把这组文件列全。我一般会把 .shp、.dbf、.prj 三个文件单独复制到工作目录其余的文件由 GIS 软件自动索引。如果遇到 .dbf 缺失属性表里的人口信息就找不回来面积字段也没了这份数据只剩图形外壳实用性大打折扣。还有一个细节容易被忽略.cpg 文件在很多共享包里并不同时提供。没有 .cpg 不代表编码一定错而是软件需要“猜”编码。猜不中的时候中文建筑类型字段就会变成乱码这一步会在后面避坑章节具体展开。2.2 坐标系先定死面积为什么经常差几十倍上海的建筑面数据常见坐标基准是 CGCS2000 或 WGS84也可能是 GCJ-02 火星坐标。这个判断不能靠猜而是要打开 .prj 文件直接看投影参数描述。最直接的验证方法是把建筑面和在线路网叠加如果建筑整体向北偏了几百米说明底图坐标与路网基准不一致如果建筑轮廓与影像吻合但面积字段算出来是天文数字说明投影坐标系设置有问题。面积计算的原理其实不复杂面积是平面量算只有在投影坐标系下单位才是平方米。如果 .prj 里面写的是经纬度ArcGIS 或 QGIS 的“计算几何”功能会提示单位是度拿度为单位算出来的多边形面积通常是个离谱的巨大数字比如一个小区建筑面算出几百万平方千米。上海这种中纬度地区我习惯把数据统一到 CGCS2000 高斯克吕格 3 度带投影例如 EPSG:4547中央经线 114°E单位是米算出来的面积才是常规认知里的平方米。针对这份下垫面数据拿到手先做一个动作读取 .prj 文件如果不是投影坐标系就先用 GIS 软件或代码重投影到 EPSG:4547再执行面积统计。千万别在经纬度坐标系下直接操作否则后面 SWMM 子汇水区的面积占比全都会错模型率定阶段的“面积不对齐”问题最难排查。2.3 属性表定稿面积字段、人口字段、建筑分类谁是谁shp 格式的 .dbf 属性表字段命名经常不受控制。有的包用 Shape_Area 存投影面积有的包只在几何里存坐标、面积字段留空需要自己算。人口字段更复杂可能有 POP、POPU、常住人口、网格人口等多种命名。拿到这份 2020 上海建筑面数据后我建议先建一个字段清点表把所有字段名过一遍确认每个字段的实际含义。字段清点至少要覆盖三类信息。第一是建筑面要素的唯一标识用来关联连接第二是面积字段判断它是预计算好的平面面积还是需要重新计算第三是人口字段这个字段的口径决定后续人口数据能不能直接落到子汇水区上。常见的人口字段可能是统计口径人口也可能是基于手机信令或居住空间估算的夜间人口两者在数量级上可能差一到三倍。城市建筑和农村建筑在属性表里一般有分类编码这一步也要确认编码含义不然后面统计不透水面积时会把农棚误判成住宅。一个习惯我每次都保留读入数据后先跑一遍统计看最大面积、最小面积、人口字段总和再和行政区统计年鉴对一下。人口总和差太多的时候不要怀疑数据坏了先怀疑字段口径。这一轮检查做完才轮到真正的下垫面操作。3. 从 shp 到 WKT 导出子汇水区切割、面积占比与 SWMM 衔接实操3.1 QGIS 里先做预处理重投影、修复几何、按子汇水区裁剪拿到建筑面 shp 格式数据后先把建筑面和子汇水区 shp 一起拖进 QGIS。打开图层面板的属性可以看到当前坐标系如果建筑面是经纬度坐标系用 QGIS 的“重投影图层”工具目标坐标系选 EPSG:4547。子汇水区面同样要重投影到同一个坐标系两个图层坐标系不一致时空间连接结果完全不可信。接着做几何有效性检查。建筑面数据在生产和裁剪过程中经常产生细小缝隙、重叠多边形或自相交直接空间连接会漏掉一部分建筑。QGIS 里用“修复几何”工具跑一遍这一步对大面积连续建筑区尤其重要。然后是裁剪用矢量裁剪工具以子汇水区为掩膜层裁掉建筑面超出汇水区范围的部分否则建筑会跨区重复统计。预处理做完后给建筑面加一个“子汇水区编号”字段用空间连接工具按“相交”规则把子汇水区 ID 赋给建筑面。这一步是后面聚合统计的基础。如果发现部分建筑面落在子汇水区边界外但没有被裁剪掉回到几何修复那一步检查掩膜层是否有空隙。3.2 geopandas 读入、裁剪、按汇水区聚合面积代码管线适用于数据量大的情况。建筑面 shp 如果包含整个上海数量级在数十万个多边形GIS 手工操作会卡顿用 geopandas 处理更顺。以下是我在拿到数据后实际跑的管线import geopandas as gpd # 读取建筑面与子汇水区面 buildings gpd.read_file(shanghai_buildings_2020.shp, encodingGBK) subcatch gpd.read_file(subcatchments.shp) # 统一投影到 CGCS2000 高斯克吕格 3 度带单位米 if buildings.crs is None: buildings buildings.set_crs(EPSG:4547) else: buildings buildings.to_crs(EPSG:4547) subcatch subcatch.to_crs(EPSG:4547) # 修复无效几何避免裁剪时丢失要素 buildings[geometry] buildings.geometry.buffer(0) # 用子汇水区裁剪建筑面去掉汇水区之外的建筑 clipped gpd.clip(buildings, subcatch) # 按子汇水区编号聚合建筑面积 stats clipped.groupby(SUB_ID).agg( bld_area(geometry, lambda g: g.area.sum()), bld_count(geometry, count) ).reset_index() # 导出面积占比供 SWMM 建模使用 stats.to_csv(bld_area_by_subcatch.csv, indexFalse, encodingutf-8-sig)这段代码有几个关键点。set_crs只是声明坐标系不做坐标变换to_crs才是真正的重投影。如果 shapely 对象已经读到坐标但原 shp 没写 .prj 文件crs会是空的这时候只能按数据来源手动声明坐标系。buffer(0)是修复微小拓扑错误的常用做法它能把多边形自相交的无效几何重置为有效状态代价是单个多边形的面积会有微米级变化对下垫面统计影响可以忽略。聚合那一步lambda g: g.area.sum()是按汇水区 ID 计算所有建筑面要素的面积总和单位是平方米。为什么要自己重算面积而不是直接用属性表里的 Shape_Area 字段因为 Shape_Area 是数据生产方在原始坐标系下计算的坐标系变换后面积值不再准确重算一次最稳妥。导出 CSV 时用encodingutf-8-sig是为了 Excel 直接打开不出现中文乱码。3.3 把面要素批量导出为 WKT衔接 SWMM 与 PostGIS建筑面 shp 要用于 SWMM 建模时很多时候需要把建筑物轮廓直接写入 SWMM 的 .inp 文件或者是导入 PostGIS 做空间运算。这时把 shp 格式矢量数据导出为 WKT 字符串是最通用的做法。WKT即 Well-Known Text是一种纯文本表示空间几何的格式一个建筑面表现为形如POLYGON((x1 y1, x2 y2, ...))的字符串。geopandas 的 GeoSeries 自带to_wkt()方法一步就能把整个图层的几何转成文本序列# 承接上文裁剪结果把几何转成 WKT clipped[wkt] clipped.geometry.to_wkt() # 只保留子汇水区编号、建筑类型与 WKT 文本 export_cols [SUB_ID, TYPE, wkt] clipped[export_cols].to_csv(buildings_wkt.csv, indexFalse) # 打印一条看看结构 print(clipped[export_cols].iloc[0][wkt])导出的 WKT 文本可以直接用于 SWMM 的 polygon 写入也可以插入 PostGIS 的 geometry 字段。需要注意两个边界。第一WKT 坐标的浮点精度由原始数据决定默认保留小数点后 15 位这会让文件膨胀。对 SWMM 建模来说坐标保留 6 位小数足够可以在导出前用clipped.geometry.to_wkt(rounding_precision6)控制输出长度。第二WKT 不带坐标系信息字符串到了别的软件里默认按什么坐标系理解要自己声明。我一般把导出的 CSV 文件名写成shanghai_buildings_2020_epsg4547_wkt.csv把坐标系直接固化在文件名里避免后面自己都忘了坐标基准。建筑面的 WKT 在 PostGIS 里还可以做叠加分析比反复读 shp 文件快很多。把buildings_wkt.csv用COPY命令导入表再建 geometry 字段用ST_GeomFromText(wkt, 4547)转成空间对象之后任意子汇水区范围查询都不需要重新读盘。3.4 SWMM 下垫面衔接建筑面如何参与 land use 分配SWMM 子汇水区里的下垫面被简化为三类有洼蓄不透水区、无洼蓄不透水区、透水区。建筑面属于典型的不透水区而且通常属于“有洼蓄不透水区”因为城市建筑周边一般都有排水设施和硬化铺装。把建筑面 shp 数据和子汇水区做空间连接后每个子汇水区得到一个建筑面积总和这个数值就是不透水面积的主要分量。实际操作中把建筑面积除以子汇水区面积得到的是建筑面占比。这个占比不会直接等于 SWMM 里的 Imperv 百分比因为道路、广场、停车场等地表硬化面积没有统计在内。需要把建筑面、道路面等矢量数据合并计算不透水总占比建筑面是其中权重最大的分母。下面这段是 SWMM 子汇水区参数表的常见折算方式子汇水区 ID总面积m²建筑面积m²建筑占比估算 Imperv 百分比S11250003860030.9%52%S2980002150021.9%43%建筑占比给出来之后Imperv 取值还要叠加道路和铺装面数据才能定。如果没有额外的道路数据用建筑占比乘以一个 1.4 到 1.8 的扩张系数做估算这是常见的粗糙做法仅适合规划阶段施工图阶段必须补道路下垫面数据。4. 避坑指南这份建筑 shp 最容易踩的五个坑4.1 坑一面积字段变成天文数字现象在 ArcGIS 里打开属性表Shape_Area 字段的值达到几百万甚至几十亿明显不符合常识。用 QGIS 统计建筑面积单位直接体现为“度”没法看。原因这份建筑面在原始坐标系下是经纬度坐标生产方预生成的面积字段没有经过投影坐标转换直接用地理坐标计算的面积单位是平方度不是平方米。解决先读 .prj 判断原始坐标系再重投影到 EPSG:4547 这类高斯克吕格投影坐标。投影后在属性表里新增一个双精度字段用“计算几何”重新计算面积用代码管线则走to_crs后重算g.area。之后所有面积统计都基于新字段不要再用原始 Shape_Area。4.2 坑二人口字段总和与统计年鉴差三倍现象人群口字段求和得出上海人口几千万跟统计年鉴出入很大。有的片区人口密度异常高有的片区几乎为零。原因人口字段口径不一致。有的字段存的是“居住空间估算人口”基于建筑楼板面积和户数折算有的是“夜间人口”基于手机信令还有的是第七次人口普查口径。建筑属性和统计口径混合在一张表里直接求和当然对不上。解决先对每个街道或镇域做一个总量核对把 shp 里人口字段按行政区划代码聚合逐年鉴数据对比。差距过大时用街道级控制数给建筑人口字段做线性缩放保证总量在合理范围。SWMM 建模中人口主要用于节点入流估算不需要极度精确但数量级必须对。4.3 坑三建筑面与子汇水区边界不贴合导致漏统计现象空间连接后若干子汇水区的建筑面积为零或明显偏小边缘建筑被切出去部分建筑悬在汇水区外。原因建筑面数据和子汇水区来自不同生产批次轮廓线之间有微小缝隙直接用 intersects 连接时一个位于边界缝隙上的多边形可能因几何相交面积过小而未被捕获。解决裁剪前先对建筑面做buffer(0.5)扩张让边界处多出半米重合区域再裁剪。统计时不按空间连接而是先裁剪再按 SUB_ID 聚合裁剪后的几何面积就是建筑在汇水区内的真实投影面积。注意在统计完成后做一次随机抽样验证挑几个汇水区手动目检边界处建筑有没有被计入。4.4 坑四dbf 中文乱码与字段名截断现象用 Python 读取 shp 包时建筑类型或区县名字段显示为乱码字段名也只剩前十个字符比如“城市农村分类”被截成“城市农村分”。原因dbf 文件用 GBK 编码存储中文而 geopandas 在部分版本下默认以 UTF-8 读取同时 dbf 规范对字段名长度限制为 10 字节超长部分被截断。解决读文件时显式指定编码。gpd.read_file(...shp, encodingGBK)。如果仍然乱码说明源文件可能是 UTF-8换成encodingutf-8再试。字段名截断无法解决只能在读取后自行重命名。为了避免后续代码里字段名混乱我拿到包的第一件事就是把 dbf 里的所有字段重命名为拼音或英文短名后续脚本全部基于重命名后的字段执行。4.5 坑五农村建筑把农棚、生产用房算成了住宅现象农村子汇水区里不透水面积占比很高甚至比城市中心区还高径流系数明显不合理。原因这份数据里农村建筑面除了住宅还包括农棚、养殖棚、仓储用房等生产性建筑。属性表如果区分了“农村建筑”和“城市建筑”但没区分“住宅”和“生产建筑”统计时全部当作建筑不透水面处理。解决查看建筑类型编码表把属性字段里的生产用房单独拆出。SWMM 里农棚和养殖棚的径流特性与住宅不同前者屋面材质多为彩钢或简易材料一部分雨水会进入棚内地面有效不透水率要打折我用时通常按住宅的 0.7 到 0.8 折减处理。不做这一步农村片区径流系数会系统性偏高。5. 进阶技法用建筑占比反推 SWMM 不透水率并做交叉验证最后一个环节是把聚合好的建筑面数据变成 SWMM 可以直接用的 Imperv 参数并且验证取值是否在合理区间。这个步骤我每次都放在模型参数率定之前因为 Imperv 定了地表产流计算才站得住。先从聚合结果开始。承接第 3.2 节的stats表分子汇水区做占比计算import geopandas as gpd import pandas as pd # 读取子汇水区与建筑面聚合结果 subcatch gpd.read_file(subcatchments.shp).to_crs(EPSG:4547) stats pd.read_csv(bld_area_by_subcatch.csv) # 合并子汇水区总面积 subcatch[sub_area] subcatch.geometry.area merged stats.merge(subcatch[[SUB_ID, sub_area]], onSUB_ID) # 建筑面占比 merged[bld_pct] merged[bld_area] / merged[sub_area] * 100 # 按经验扩张系数估算不透水率系数取 1.5仅规划阶段可用 merged[imperv_est] (merged[bld_pct] * 1.5).clip(upper90) # 输出 SWMM 子汇水区参数表 merged.to_csv(subcatch_imperv_est.csv, indexFalse)扩张系数的选择要有依据。城市中心区道路和铺装密集系数取 1.6 到 1.8郊区路网稀疏取 1.2 到 1.4。clip(upper90)是为了防止建筑占比极高的商业区把 Imperv 推到 100%实际地表总有透水缝隙和绿化。这个估算法只做初值不能替代实测数据。用了这版上海建筑面 shp 做下垫面后我做了一层交叉验证把按建筑占比估出来的 Imperv与各子汇水区在卫星影像里随机抽样的裸眼不透水率做对比两者误差在正负 8 个百分点以内。如果某个片区偏差超过 15 个百分点基本可以断定是建筑分类没拆干净回去检查农棚和生产用房字段。从那以后我每次拿到任何行政部门 shp 包都会强制先走一遍坐标系检查、编码确认、字段口径核对、面积重算这四步不再用任何带预生成面积字段的原始数据直接建模。这套顺序就是从这份 2020 上海建筑面数据开始的它把我建模前那一整个晚上的手工描图时间压缩到十分钟以内。希望帮到你。本文还有配套的精品资源点击获取
返回列表