简介:面向城市规划、地理信息、应急管理与环境工程等领域的从业者和学生,这份2020年上海建筑物面数据(含城市与农村)可补足下垫面信息缺失、矢量化成本高的痛点,以标准shp格式封装,能直接在ArcGIS、QGIS中加载使用,适用于下垫面分析、SWMM建模、建筑能耗模拟及城乡发展评价。压缩包共6个文件,总大小约108.78MB,核心为shp建筑面要素层,配套dbf属性表记录面积与人口信息,shx提供空间索引,prj定义坐标系,cpg与xml则负责字符集和元数据说明,结构简洁但字段基本满足建模需要。浏览/学习人数已有621人,108.78MB的体量也侧面反映建筑轮廓细化程度,可用作数据精度参考。借助该数据,使用者无需重新采集即可获得覆盖上海城乡的建筑面要素,可按区县、格网裁剪合并,快速构建SWMM汇水区参数、分析屋顶可用潜力、推算人口空间分布,也可结合面积字段提取不透水率并评估内涝风险暴露程度,为规划决策节省大量前期处理时间。
1. 把建筑轮廓shp送进SWMM之前:这份数据到底帮你省了什么
做SWMM项目的人大概都有同感:管网模型调参再玄学,好歹有套路,真正让人翻车的反而是建模第一步——下垫面数据。你要给汇水区赋不透水率,得知道每一块建筑屋顶、院子、道路到底占多少面积;你要做溢流节点检查,得知道这片汇水区里的人口和建筑密度。没有可靠的面状数据,整个模型的产流参数就是空中楼阁。这份2020年上海建筑物面数据(城市+农村),正好补齐这块拼图。
它是一份shp格式的矢量数据,要素是建筑物面,属性里带着面积和人口信息,覆盖上海全域,城市和农村的建筑都在里面。对SWMM建模来说,它的直接价值是:你不用再靠目估或者手工描建筑轮廓来推算不透水面积,直接把建筑面叠进汇水区,按属性统计就行。适合三类人:一是做上海片区SWMM模型的工程师,二是需要给排水模型提供下垫面底图的规划人员,三是做洪涝风险评估、需要快速估算建筑密度和人口分布的研究者。数字一个都不用你自己编,矢量面套上去,该有的都有了。
2. 建筑面shp和SWMM的产流参数:为什么它能顶替半天的外业测量
2.1 不透水率的底层逻辑:面积统计不是目的,产流参数才是
SWMM的径流模拟质量,很大程度上取决于子汇水区不透水率设得准不准。不透水率不是拍脑袋拍出来的,它来自“屋顶+道路+停车场”这类真正不透水的表面积占总面积的比例。建筑面shp给你的就是其中最大的一块——屋顶。剩下还有道路、广场这些,但建筑通常占了城市建成区不透水面的绝对大头。
拿到这份shp,你可以在GIS里把每个汇水区内的建筑面面积累加出来,除以汇水区总面积,得到“建筑不透水贡献率”。实际建模时,我一般会在这个基础上再叠道路中心线或者用地分类,把道路比例补进去,得到一个更完整的子汇水区不透水率。这里有一个经常被忽略的细节:SWMM的Subcatchment属性里,%Imperv填的是整个汇水区的不透水比例,不是建筑占汇水区面积的比例。如果你直接把建筑覆盖率填进去,产流量会偏小,因为道路和广场没算进去。
这份shp的价值就在于“建筑底数”这一层是可靠的。农村区域尤其明显——农村房屋尺度小、形状不规则,手工描一遍轮廓动辄大半天,有些房顶还是彩钢瓦、阳光棚这类半透水材料,目估误差很大。有了矢量面数据,你至少能把“建筑轮廓”这一项钉死,剩下的误差来源就只有“材料透水性”这个经验参数了。
2.2 面积字段和人口字段:一个用于产流,一个用于水质和旱季流量
属性表里如果带面积字段,那你不用自己计算几何,省了一步;人口字段则能撑起两个用途:一是SWMM水质模拟里的污染物累积和冲刷——人口密度直接关联生活垃圾和污水产量;二是旱季流量(DWF)的估算——你把人口字段落到各个汇水区,乘以人均排水量系数,能算出基础污水量,这部分很实用。
常见做法是:用空间连接(Spatial Join)把建筑面的面积和人口字段按汇水区边界聚合,产出两个统计量——建筑总面积和总人口。需要说明的是,shp属性表里的“人口”大概率是按建筑属性(比如住宅楼层数、建筑面积)估算出来的,不是普查口径的实住人口。用它做区域间对比、估算量级没问题,但要落到具体社区人数,建议还是用街道/居委的人口统计去做校验。
2.3 坐标系陷阱:WGS84还是CGCS2000,直接影响面积量算
打开shp第一件事不是看属性表,而是看投影坐标系。如果数据是WGS84地理坐标系(经纬度),直接算面积会得到一个离谱的数字,因为经纬度坐标没有投影变形校正。正确做法是先投影到适合上海的区域投影坐标系——常见用CGCS2000 / 高斯-克吕格投影,上海一般落在119E或121E分带附近,也可以直接用WGS 1984 UTM Zone 51N。
这一步别偷懒。我见过有人在ArcGIS里直接算面积,出了几百万平方米的“建筑”——误差放大十倍以上,原因是几何计算用decimal degrees直接跑。正确的操作路径是:先定义投影,再用Project工具转投影,最后用Calculate Geometry算面积。如果你的属性表已经带了面积字段,不代表它一定是用投影坐标算出来的,建一个临时字段用投影后几何复核一遍是最稳妥的。
3. 把shp喂给SWMM:从GIS预处理到inp文件的完整操作流
3.1 GIS端的预处理:清洗、投影、字段规整
拿到原始shp,先做三件事:检查要素类类型、确认坐标系、看属性表结构。建筑面一般是Polygon要素,字段里通常有建筑ID、面积、人口这几列。运行以下PyQGIS脚本可以快速规整字段并导出备用:
# 在QGIS Python控制台或PyQGIS脚本中运行 # 目的:检查字段名、坐标系,并生成一个仅保留关键字段的副本 layer = iface.activeLayer() # 当前图层必须是建筑面shp print(layer.crs().authid()) # 输出坐标系,比如EPSG:4326代表WGS84经纬度 # 新建字段:用投影后的几何计算面积(平方米) from qgis.core import QgsField, QgsVectorFileWriter from PyQt5.QtCore import QVariant # 给图层添加area字段 layer.dataProvider().addAttributes([QgsField("area_m2", QVariant.Double)]) layer.updateFields() # 逐要素计算几何面积 with edit(layer): for feat in layer.getFeatures(): geom = feat.geometry() feat.setAttribute("area_m2", geom.area()) # geom.area()返回的是投影后的面积 layer.updateFeature(feat) # 导出为新的shp,只保留字段:OBJECTID、area_m2、POPU(如果有) options = QgsVectorFileWriter.SaveVectorOptions() options.driverName = "ESRI Shapefile" transform_context = QgsProject.instance().transformContext() QgsVectorFileWriter.writeAsVectorFormat( layer, "/path/to/output/building_clean.shp", transform_context, options)这段脚本的逻辑分三层:先打印坐标系,确认数据是经纬度还是投影坐标;然后给每个建筑要素新增一个面积字段,按当前几何计算面积——注意如果图层还是WGS84地理坐标系,这一步算出来的面积依然是错的,必须先做Project变换;最后只导出需要的字段,去掉冗余列,避免后面空间连接时字段名冲突。
实际处理时,我会把“投影变换”放在添加字段计算之前执行。之前遇到一份数据,坐标系定义是CGCS2000,但实际坐标数值是经纬度,项目文件里却标成了投影坐标——这是shp常见病:.prj文件内容和实际几何不符。遇到这种情况,先看坐标值范围:如果经度在120左右、纬度在31左右,即使.prj写着投影坐标,也要手动按WGS84定义再转投影。
3.2 空间连接汇水区:面积和人口按区聚合
SWMM建模的下一步是给每个子汇水区算建筑覆盖率。你需要一个汇水区边界shp,然后用空间连接把建筑面的面积、人口聚合到汇水区上。ArcGIS的Spatial Join可以选聚合规则为SUM——即统计每个汇水区内建筑的面积总和与人口总和。下面用QGIS表达式引擎做同样的工作:
-- 在QGIS的“按位置汇总”工具中执行 -- 目的:对每个汇水区多边形,汇总落入该区内的建筑面积和人口 -- 输入:subcatchment(目标图层)、building_clean(连接图层) -- 聚合方式:sum(area_m2)、sum(POPU) SELECT sub.id AS sub_id, SUM(b.area_m2) AS bldg_area, SUM(b.POPU) AS bldg_popu, COUNT(b.id) AS bldg_count FROM subcatchment sub LEFT JOIN building_clean b ON ST_Within(ST_PointOnSurface(b.geometry), sub.geometry) GROUP BY sub.id这段SQL的逻辑核心是ST_PointOnSurface——取建筑面的质心点判断是否落在汇水区内。为什么不用ST_Intersects?因为建筑边界和汇水区边界常常共享一段线,用面-面相交会重复计数,一栋建筑骑在两个汇水区边界上会被算了两次。用质心则保证每栋建筑只归入一个汇水区,这个细节能避免后续SWMM模型里出现面积重复计入的问题。
这里还要强调一个计数陷阱:shp的字段名最长10个字符,如果原始字段叫AREA_M2已经超了就需要改短。Spatial Join成功后检查结果表,如果出现大量建筑总面积为0的汇水区,多半是建筑层和汇水区边界在空间上没有完全套合,比如建筑超出了汇水区边界线。这时用“按位置选择”里的“与源图层相交”做一次诊断性选择,看是不是有几十栋建筑悬空在汇水区外面。
3.3 生成SWMM的inp文件:把统计结果变成Subcatchment属性
建筑覆盖率算好后,手动在SWMM里一个一个填Subcatchment属性太累了,尤其是有几十上百个子汇水区的时候。常见做法是先把属性表整理成CSV,用Python生成inp文件片段,或者直接操作swmm-api这类库来写。下面是生成Subcatchment段落的基础代码框架:
# 读取汇水区统计CSV,生成SWMM .inp文件的[SUBCATCHMENTS]段 # CSV字段:SubName,Outlet,Area_ha,%Imperv,Width,Slope import csv # inp文件的subcatchment段格式: # Name Outlet Area %Imperv Width Slope # 注意:面积单位是公顷,%Imperv是百分数 rows = [] with open('subcatch_stats.csv', 'r') as f: reader = csv.DictReader(f) for r in reader: # 建筑覆盖率转不透水率:这里按建筑占汇水区面积比例作为不透水率下限 imp = float(r['bldg_area_ha']) / float(r['area_ha']) * 100 # 实际建模时再加道路不透水比例 imp_total = min(imp + 10.0, 95.0) # 道路/广场按10%估算 rows.append(f"{r['sub_id']} {r['outlet_id']} {r['area_ha']} {imp_total:.1f} {r['width']} {r['slope']}") with open('subcatchments.inp', 'w') as f: f.write("[SUBCATCHMENTS]\n") f.write(";;Subcatchment Outlet Area %Imperv Width Slope\n") f.write("\n".join(rows))这段代码干的事很简单:把CSV里每行的建筑覆盖率换算成SWMM要的%Imperv——注意这里做了一件事,就是加了一个10%的道路/广场估算值,上限卡在95%。原因是不透水率超过95%在物理上不太可信,而且SWMM对超100%的值会直接报错。宽度(Width)和坡度(Slope)这两个参数代码里直接从CSV读,实际项目中它们通常来自汇水区几何计算或地形数据。
用这个办法,几十个汇水区的inp段落几秒钟就能拼出来。生成后直接用SWMM打开inp文件,检查有没有“Node not found”或“Subcatchment outside of study area”这类报错。特别提醒:inp文件里的单元编号不能重复,Outlet名称必须和节点表里的ID完全一致,包括大小写——SWMM的ID匹配是区分大小写的,这个坑我至少踩过三次。
4. 避坑指南:shp建筑面数据的六个常见炸点
4.1 坐标系定义错了,算出来的面积和人口密度全是错的
现象:在GIS里打开shp,建筑图形位置和底图对不上,歪了几百米;或者直接用工具算面积,出来的数值大得离谱。
原因:shp的.prj文件缺失或标注错误,实际坐标是GCJ02或BD09偏移坐标系,而软件按WGS84去理解;或者坐标是经纬度但被当成了投影坐标。
解决:先看坐标数值范围判断真实坐标系,再检查.prj。如果图形偏移量是固定的几百米,多半是GCJ02偏移——这种国内测绘数据很常见。处理方式是:识别出真实坐标系,用已知控制点做校正,或者直接放弃原坐标,用底图上的地标做视觉配准。上海地区我一般统一转到CGCS2000 高斯投影。
4.2 字段名截断导致后续脚本报错
现象:Python脚本读字段报KeyError,或者明明CSV里有Column叫POPULATION,程序却说找不到。
原因:shp的dBASE格式硬性限制字段名最长10字符,ArcGIS/QGIS在保存时会自动截断或重命名,原始长字段名不保留。
解决:拿到shp后先看属性表列名,凡是超过10字符的字段,在导出前手动改成短名。如果已经截断了,用QGIS的Refactor Fields工具批量重命名字段再导出。我习惯在一开始就用“code_10”这样的短字段规划属性结构,避免后面所有脚本都要跟着改名。
4.3 建筑面和汇水区边界不对齐,统计值系统性偏小
现象:Spatial Join之后,沿主干道的汇水区建筑覆盖率异常低,明显和航拍图不符。
原因:建筑shp和汇水区shp来源不同,边界线存在几米到几十米不等的套合误差,导致靠近边界的建筑被分到了相邻汇水区,或者干脆悬在外面没被统计到。
解决:先做拓扑检查,看建筑要素是否完整落在汇水区边界内。对于骑行误差不大的数据,可以用缓冲带(Buffer)处理后重新切割汇水区——我一般给建筑面设定0.5到1米的负缓冲(向内收缩),消除边界贴合误差;对误差大的数据,直接以建筑层为基准重新描绘沿街汇水区边界。这套操作虽然繁琐,但面积统计的准确性能上一个台阶。
4.4 面积字段不是平方米,单位不一导致SWMM报错
现象:SWMM运行时提示Subcatchment面积过大,或者总径流量异常偏大。
原因:原始shp里AREAFIELD的单位可能是平方公里或公顷,直接导入后没换算。
解决:在代码里统一单位换算,面积全部转成公顷(hectare)再进inp。SWMM的Area单位是公顷(acres也可,看项目设置),如果你按平方米填进去,数值放大一万倍,水文过程线会像山洪暴发。检查方法很简单:把所有子汇水区面积加起来,和整个研究区面积对比,如果差十倍以上,基本是单位问题。
4.5 人口字段口径不清,DWF估算失真
现象:用人口字段估算旱季流量,结果比污水处理厂进水量大了好几倍。
原因:shp的属性表里“人口”可能是住户数×户均人数粗算出来的,也可能是建筑面积除以人均住房面积得到的,和实际常住人口是两回事。上海中心城区和老旧小区这种偏差尤其明显。
解决:用分区人口统计校准。拿到shp后,先按街道或行政区汇总人口,去和统计年鉴的常住人口比对——通常需要乘以一个0.6~0.85的修正系数,抵消“一房多人多户但shp只算一套”导致的高估。做SWMM模型的旱季流量输入时,建议单独建一个人口修正字段,不要直接用原始字段。
4.6 数据版本和现状不符,新建小区建筑缺失
现象:模型里某一片汇水区建筑覆盖率特别低,但实地看已经建成了新楼盘。
原因:数据是2020年的,但现在已经是好几年后,新建建筑自然不包含在里面。
解决:叠加最新卫星影像或路网数据做增量更新。在GIS里把项目区的现状影像作为底图,新建建筑手动补充画边界,再合并进原图层。上海这种城市更新速度快的地方,数据新鲜度的影响比坐标系还大。SWMM模型率定完之后,如果监测流量长期低于模拟值,不妨先怀疑是不是下垫面数据缺了新建建筑,而不是急着调参数。
5. 进阶用法:用建筑面数据反推不透水率子区划分和产流参数
5.1 按建筑密度聚类划分子汇水区
SWMM建模时子汇水区怎么划,直接影响率定成本。城市片区常见的做法是:按用地性质或道路边界切分汇水区,但如果你手里只有建筑面这份数据,也可以直接用建筑密度做聚类,划分“高密度建筑区、中密度建筑区、低密度建筑区、农村散居区”四种类型,每类赋一套产流参数。和按道路切割的方法相比,按密度聚类的优势是每一类汇水区的产流参数物理意义更明确,率定时调整参数也更好解释。
具体实现可以用GIS里的核密度分析工具,提取建筑面中心点做KDE(核密度估计),然后重分类成四级。带宽参数一般用100米左右——太密了全是马赛克,太疏了又看不出老街区和新建区的差异。然后用重分类后的栅格转面,再和汇水区边界做裁剪。每一类区域给一个初始%Imperv:高密度建成区80~90%,中密度建成区65~80%,低密度区40~60%,农村散居区20~40%。
有一个经验值得记:上海老城厢那种密集低层建筑区,建筑密度高但屋顶面积小,和老式花园洋房区的产流特征完全不同——前者虽然建筑覆盖率高,但巷道窄、绿化少,汇流时间短;后者建筑覆盖率不高,但有大面积庭院绿地和透水铺装,汇流时间反而长。所以建筑密度分区不能替代汇流时间参数的分组,两套参数要分别率定。
5.2 人口数据在SWMM水质模拟里的落地
SWMM水质模块里的污染物累积曲线通常用人口密度作为关键驱动参数——生活垃圾、污水管道溢流污染都和人口直接相关。建筑面shp里的人口字段可以按汇水区聚合成人口密度,然后映射到Land Use类型的污染物累积参数上。具体操作是:把每个子汇水区的人口密度算出来,分成低、中、高三档,分别对应SWMM里Land Use的不同堆积速率参数。
常见做法是给每个Land Use类型设置不同的最大累积量(Max. Buildup)和半饱和常数(Half Saturation Constant)。人口密度高的区域,最大累积量要调大,清扫频率相应也要调整。这里有个细节:SWMM里的清扫参数是按街道清扫频率设置的,而街道清扫频率往往和人口密度正相关——高密度商业区一周扫三次,低密度居住区可能一周才一次。用人口字段推导清扫参数比拍脑袋设一个固定值要靠谱很多。
5.3 建筑层数和面积字段联合推导屋顶径流污染初始浓度
如果你那份shp属性表里恰好有建筑层数或建筑高度字段,那还能再进一步。屋顶径流的污染物初始浓度和屋面材料、老化程度关系很大,但层数是“屋顶面积=建筑占地面积×层数”这个关系里不可或缺的因子——总屋面面积越大,初期冲刷效应越明显。SWMM的洗涤(Washoff)模块里的Coefficient和Exponent参数,对于高层住宅和低层厂房应该分别设置,高层住宅屋顶面积大、坡度缓,污染物冲刷速率低;厂房坡度大,冲刷快、浓度峰值高。
用建筑面shp的方式也很直接:按建筑占地面积乘楼层数算出总屋面面积,汇总到汇水区后,把汇水区按“屋面面积占比”分档——屋面面积占比超过35%的区,洗涤速率系数取低值;低于15%的取高值。这一个分类,能让水质模拟里的SS和COD曲线和实际监测更贴近。
6. 验证方法:用三张图和一组数确认这份建筑面数据能不能用
数据拿手里先别急着建模,花半小时做四道验证工序,值回票价。
第一道:图形检验。把建筑面shp叠到卫星影像或百度底图上,随机抽三个区域:中心城区、近郊城镇、远郊农村。中心城区建筑轮廓应当和影像基本贴合,轮廓线偏差不超过两三个像素;近郊能看到成片的老式农民房,轮廓小而密;远郊应该是零散分布的独栋农宅。如果某个区域建筑密度异常稀疏或异常密集,这一片数据大概率有问题,直接跳过或另找数据补齐。这一步有个细节:影像底图时间要尽量接近数据年份,拿最新的影像去核对2020年的数据,新建筑自然会显示为空缺。
第二道:面积量算复核。用QGIS或ArcGIS对全图层计算一次总面积,和上海市统计年鉴里的“城镇建设用地面积”做量级对比。建筑占地面积一般占建设用地面积的两到四成,如果超出这个范围,检查是不是有“多部件要素”被重复计数。有人拿到shp后直接算Geometry面积,结果发现汇总面积是实际的两倍——因为数据里有大量MultiPolygon要素的环(inner ring)被当成独立面算了。正确的检查方式是Attribute Table里看每个要素的几何类型,确认是Polygon还是MultiPolygon,在统计时用ST_Area(geom)而不是ST_Area(ST_ExteriorRing(geom))。
第三道:人口总量校准。把属性表里的人口字段汇总,和2020年上海常住人口2487万比较。如果shp的“人口”是建筑容量口径,和实际值偏差20%以内都能接受;如果偏差超过50%,别直接用,需要按区县做线性校偏。操作方法:按行政区字段分组汇总shp人口,把每个区的汇总值和七普数据相除,得到每个区的修正系数,再乘回去。这个修正系数表之后做DWF估算和污染负荷计算都能复用,建议存成CSV放在项目目录里。
第四道:SWMM冷启动测试。挑一个片区先建好简化管网和三个子汇水区,把不透水率设为用这份数据算出来的值,跑一场5年一遇的设计暴雨。看输出的径流总量是不是落在合理区间——上海短历时暴雨径流系数一般在0.4~0.75之间,如果总径流系数小于0.3或大于0.9,说明不透水率参数或者汇水区面积设置有比较大的误差。冷启动测试不追求和监测数据拟合,只求量级合理,这一步能拦住后期率定阶段的大量无效劳动。
从那以后,我拿到任何一份下垫面shp,第一件事就是先做上面四道工序再谈建模。校验数据本身花的半小时,只要能在率定阶段省下一周,就非常划算。希望这份上海建筑物面数据的验证思路,也能帮你少走点弯路——毕竟SWWM的参数调起来够玄学了,数据底子咱尽量别给它添乱。
本文还有配套的精品资源,点击获取