尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

北京400万栋带高度建筑物shp数据:从三维重建到城市形态指标计算

北京400万栋带高度建筑物shp数据:从三维重建到城市形态指标计算 简介这份2023年北京全域建筑物矢量数据集面向城市规划、交通设计、环境评估与灾害预防等领域的GIS从业者、研究人员及学生提供覆盖北京全域、带高度属性的建筑物空间数据可用于分析城市立体空间结构、建筑密度与天际线特征。资源包共10个文件以shp与shx构成几何主体dbf存储属性字段prj定义坐标投影xml记录元数据整体约547.19MB解压后可直接在ArcGIS、QGIS等软件中加载编辑。数据集包含超过四百万栋建筑物除平面位置外还附带高度信息便于估算市场覆盖范围、模拟光照阴影、评估灾害风险与制定疏散方案。目前已有395人学习下载适合需要高精度城市三维基础数据的中高级GIS用户参考使用。1. 北京全域建筑物矢量 shp 数据400 万栋带高度到底能拿来干什么如果你手头正好有一个北京全域建筑物矢量 shp 数据属性表里还带着高度字段总量在 400 多万栋这个量级那么你面对的已经不是一份普通的 GIS 底图而是一份可以直接喂给三维管线、能耗模型和城市形态分析的空间底座。很多做城市计算的人第一步就卡在数据上公开的建筑物轮廓要么只有面、没有高度要么只覆盖主城区要么字段残缺到没法做体块拉伸。带高度的全域建筑物 shp 恰好补上了这个缺口它让「二维轮廓」变成「三维体块」这件事从手工建模降级成一次字段映射。这份数据适合三类人做城市三维重建、需要 shp 转 3dtiles 的工程师做日照、通风、能耗、人口空间化等需要建筑体量的研究者以及做 GIS 二次开发、要把建筑数据接进自己平台的开发者。它解决的核心问题是「建筑在哪里、有多高、占地多大」不解决「建筑长什么样、什么材质、哪年建的」。先把边界说清楚后面才不会翻车。2. 先搞懂带高度建筑物 shp 的字段结构和坐标基准拿到一份建筑物 shp第一件事不是急着打开符号化而是把属性表和坐标系看清楚。400 多万栋这个体量字段设计是否合理、高度是米还是层数、有没有楼层数字段直接决定你后面能不能顺利做出三维体块。这一章把「数据里到底有什么」讲透再谈怎么用。2.1 建筑物 shp 常见字段高度、层数、面积、ID 各代表什么一份典型的带高度建筑物矢量数据属性表通常包含这几类字段唯一标识如 building_id、几何面积shape_area 或 area、建筑高度height、楼层数floor 或 levels、可能还有用途分类function/type。这里最容易踩的坑是「高度」的口径不统一——有的数据 height 是檐高有的是含屋顶构件的总高有的干脆存的是层数乘以 3 米估算出来的。你在做三维拉伸前必须抽样核对几个已知建筑比如国贸三期、央视大楼这种高度公开的地标用它们的实际高度反推字段口径。另一个高频问题是坐标系。北京地区常见的有 CGCS2000 高斯克吕格投影、北京地方坐标如北京54、以及 WGS84 经纬度。如果 shp 是投影坐标单位是米直接算面积和拉伸没问题如果是地理坐标shape_area 字段的单位会是平方度数值小得离谱这时候必须先用投影工具转成米制坐标再计算。判断方法很简单看坐标系定义里有没有「Gauss_Kruger」或「CGCS2000_3_Degree_GK_Zone」这类字样有就是投影坐标。import geopandas as gpd # 读取建筑物 shp先看坐标系和字段 gdf gpd.read_file(beijing_buildings.shp) print(坐标系:, gdf.crs) print(字段列表:, list(gdf.columns)) print(要素数量:, len(gdf)) # 抽样看高度字段的分布判断口径 print(gdf[[height, floor]].describe()) # 如果 crs 是地理坐标单位度面积字段不可信需投影 if gdf.crs and gdf.crs.is_geographic: gdf gdf.to_crs(epsg4548) # CGCS2000 3度带北京所在带按实际带号调整 gdf[area_m2] gdf.geometry.area这段代码做了三件事确认坐标系、查看高度和楼层字段的统计分布、在地理坐标情况下自动投影并重算面积。参数上epsg4548是 CGCS2000 三度带在北京一带常用的带号但具体带号要看你数据的中央经线北京大致在 117°E 附近对应三度带第 39 带EPSG 可能是 4547 或 4548务必用gdf.crs的实际定义核对不要照抄。describe()输出的 min/max 能帮你快速判断高度字段有没有异常值比如出现 0 或 9999 这种填充值。2.2 坐标基准与投影选择为什么面积字段经常对不上很多人拿到 shp 后发现 shape_area 字段和自己用几何算出来的面积差很多原因通常有三个一是数据本身是地理坐标shape_area 是软件按度算的二是数据经过多次投影转换面积字段没有同步更新三是数据用了某种等积投影但字段是历史遗留。最稳妥的做法是永远不信任自带的面积字段用当前坐标系下的几何重新计算。投影选择上做北京全域分析建议统一到 CGCS2000 三度带或北京地方投影。如果你后续要做 shp 转 3dtiles三维引擎通常要求 WGS84 经纬度或 Web Mercator这时候需要在最后一步再转而不是一开始就转成经纬度导致面积计算失真。记住一个原则分析和量算用投影坐标展示和三维发布用地理坐标或 Web Mercator中间用to_crs切换。提示400 多万栋的 shp 用 GeoPandas 直接读可能吃满内存建议先用pyogrio或fiona的过滤读取或者按区县切分后再处理。3. 从 shp 到三维体块高度字段怎么驱动建筑拉伸有了干净的字段和正确的坐标系下一步就是把二维面变成三维体块。这是带高度建筑物数据最核心的用法也是 shp 转 3dtiles 的前置步骤。这一章讲清楚拉伸的逻辑、高度字段的清洗规则以及怎么在常见工具里落地。3.1 用高度字段做体块拉伸的最小实现三维拉伸的本质是每个建筑面沿 Z 轴向上挤出 height 米底面就是建筑轮廓。在 GIS 里可以用 ArcGIS 的「图层转 3D 要素」或 QGIS 的「拉伸」渲染在代码里可以用 GeoPandas 加高度字段导出成 GeoJSON再交给三维引擎。最小实现的关键是保证 height 字段是数值型且没有空值。import geopandas as gpd import numpy as np gdf gpd.read_file(beijing_buildings.shp) # 高度字段清洗空值用楼层数估算异常值截断 gdf[height] pd.to_numeric(gdf[height], errorscoerce) # 空值用楼层数 * 3 米补 mask_null gdf[height].isna() gdf[floor].notna() gdf.loc[mask_null, height] gdf.loc[mask_null, floor] * 3 # 仍然为空或为 0 的给一个默认低层高度 gdf[height] gdf[height].fillna(6) gdf.loc[gdf[height] 0, height] 6 # 异常高值截断北京最高建筑约 528 米超过 600 视为错误 gdf.loc[gdf[height] 600, height] 600 # 导出带高度的 GeoJSON供三维引擎拉伸 gdf[[building_id, height, geometry]].to_file( beijing_buildings_height.geojson, driverGeoJSON )这段代码的核心是高度清洗三步空值用楼层数乘 3 米补全、零值和负值给默认 6 米、超过 600 米的截断。参数上floor * 3是住宅和办公的常见层高估算如果你知道数据里 floor 是准确的可以按 3.2 或 3.5 调整默认 6 米是给那些既没高度也没楼层的小体量建筑一个合理下限避免拉伸成零高度导致三维里看不见。导出 GeoJSON 时只保留必要字段能显著减小文件体积400 万栋全字段导出可能几个 GB裁剪字段后能压到几百 MB。3.2 shp 转 3dtiles 的两种路径与参数取舍把建筑物 shp 变成 3dtiles常见两条路一是用 Cesium ion 或类似平台上传 GeoJSON 自动切片适合快速验证二是本地用工具链如3d-tiles-tools、py3dtiles自己切适合数据敏感或需要批量处理的场景。两条路的核心参数都是「高度字段映射」和「切片层级」。本地切片时你需要先把 shp 转成带高度的 GeoJSON 或 CityGML再用工具生成 3dtiles。关键参数是几何误差geometric error和最大切片深度。几何误差设太小切片数量爆炸加载慢设太大远处建筑糊成一团。北京全域 400 万栋建议几何误差从 20 米起步最大深度控制在 18 层左右具体要看你的展示范围和硬件。参数建议值说明几何误差20~50 米越小越精细切片越多最大深度16~20控制瓦片树层数高度字段height必须是数值型坐标输出WGS843dtiles 通常要求经纬度注意shp 转 3dtiles 前务必确认高度单位是米。如果数据高度是英尺或层数直接切片会得到一堆比例错误的体块这种错误在三维场景里非常显眼但排查起来很费时间。4. 400 万栋数据的性能优化渔网分割与分区处理400 多万栋建筑无论你是做空间分析还是三维发布一次性处理都会遇到内存和性能瓶颈。这一章讲怎么用渔网分割和分区策略把大问题拆小这也是处理省级、流域级矢量数据时的通用手法。4.1 用渔网分割 shp 降低单次处理量渔网分割的思路是生成一个规则网格把建筑物按所在网格切分每个网格单独处理最后再合并结果。这样做的好处是单次处理的数据量可控失败时只需重跑某个网格不用从头再来。在 QGIS 里可以用「创建网格」加「按位置选择」在代码里可以用 GeoPandas 的overlay或sjoin。import geopandas as gpd from shapely.geometry import box import numpy as np gdf gpd.read_file(beijing_buildings.shp) minx, miny, maxx, maxy gdf.total_bounds # 生成 10km x 10km 渔网 cell 10000 # 米 cols int(np.ceil((maxx - minx) / cell)) rows int(np.ceil((maxy - miny) / cell)) cells [] for i in range(cols): for j in range(rows): cells.append(box(minx i*cell, miny j*cell, minx (i1)*cell, miny (j1)*cell)) grid gpd.GeoDataFrame({cell_id: range(len(cells))}, geometrycells, crsgdf.crs) # 空间连接给每栋建筑打上网格编号 joined gpd.sjoin(gdf, grid, howleft, predicateintersects) # 按网格分组导出 for cid, group in joined.groupby(cell_id): group.drop(columnsindex_right).to_file( ftiles/building_tile_{cid}.shp, driverESRI Shapefile )这段代码先生成 10 公里见方的渔网再用空间连接给每栋建筑打上网格编号最后按网格导出。参数上cell10000是 10 公里北京全域大约能切成几十个网格每个网格几万到十几万栋单次处理压力小很多。如果你的机器内存更小可以把 cell 调到 5000如果要做区县级汇总直接按行政区划切分更合适。predicateintersects保证跨网格边界的建筑至少被分到一个网格不会丢失。4.2 分区处理后的合并与去重分区处理完合并时最容易出现的问题是边界建筑重复。因为一栋建筑可能同时与两个网格相交sjoin会把它分到两个网格里。合并前必须按唯一 ID 去重否则统计建筑总数时会偏大。去重逻辑很简单合并所有分片后按 building_id 保留第一条。import pandas as pd import glob files glob.glob(tiles/building_tile_*.shp) parts [gpd.read_file(f) for f in files] merged gpd.GeoDataFrame(pd.concat(parts, ignore_indexTrue), crsparts[0].crs) # 按唯一 ID 去重避免边界建筑重复 merged merged.drop_duplicates(subsetbuilding_id, keepfirst) print(去重后建筑总数:, len(merged)) merged.to_file(beijing_buildings_merged.shp, driverESRI Shapefile)去重后建议核对总数是否接近 400 万这个量级。如果差太多可能是 building_id 不唯一或存在空值需要先检查 ID 字段质量。合并后的 shp 如果还要继续用建议转成 GeoPackage 或 Parquetshapefile 单文件 2GB 上限在 400 万栋面前很容易触顶。5. 避坑与排查建筑物 shp 处理中最容易翻车的五件事这一章是我自己在处理大体量建筑物矢量数据时踩过的坑按「现象 → 原因 → 解决」写清楚。很多问题不是技术难而是数据本身的玄学提前知道能省下大量返工时间。5.1 高度字段全是 0 或空值现象打开属性表height 字段大面积是 0 或 null三维拉伸后建筑全是平的。原因数据生产时高度信息缺失或者高度存在另一个字段里没被识别。解决先检查有没有 floor、levels、层数这类字段用层数乘层高补全如果确实没有考虑用建筑面积和用途做回归估算或者接受这份数据只能做二维分析。5.2 坐标系定义丢失导致位置偏移现象把 shp 叠加到在线底图上建筑整体偏移几百米甚至几公里。原因shp 的 .prj 文件丢失或坐标系定义错误软件按默认坐标处理。解决找数据提供方确认原始坐标系或者用已知地标反推偏移量。如果 .prj 丢了但你知道是 CGCS2000 三度带可以手动定义坐标系后重新投影。5.3 面积字段单位是平方度现象shape_area 字段数值只有零点几明显不是平方米。原因数据是地理坐标面积按度计算。解决投影到米制坐标后重新计算面积不要直接用原字段。这个坑在做容积率、建筑密度分析时特别致命会导致结果差几个数量级。5.4 渔网分割后建筑总数对不上现象分区处理再合并建筑总数比原始数据多。原因跨网格边界的建筑被重复计入。解决合并后按唯一 ID 去重。如果 ID 不唯一先用几何哈希或坐标组合生成临时唯一键。5.5 shp 转 3dtiles 后建筑悬空或入地现象三维场景里建筑底部不贴地有的悬空有的扎进地里。原因地形高程和建筑底面高程基准不一致或者拉伸时底面 Z 值没归零。解决确认建筑数据的基准高程必要时用 DEM 采样每个建筑底面的地面高程把体块底面贴合地形。这一步在山区或地形起伏大的区域尤其重要。6. 进阶用法用建筑物高度数据做城市形态指标计算把数据跑通之后真正体现价值的是用它算出可量化的城市形态指标。这一章给一个具体技巧用建筑高度和占地面积计算容积率和建筑密度再按渔网聚合得到空间化的开发强度图。这套方法可以直接复用到其他城市只要你有带高度的建筑物 shp。核心公式很简单容积率 总建筑面积 / 用地面积建筑密度 建筑占地面积 / 用地面积。总建筑面积用每栋建筑的占地面积乘楼层数或高度除以层高再求和。下面这段代码按 500 米渔网聚合输出每个网格的容积率和建筑密度。import geopandas as gpd import numpy as np import pandas as pd gdf gpd.read_file(beijing_buildings_merged.shp) gdf gdf.to_crs(epsg4548) # 投影坐标单位米 gdf[footprint] gdf.geometry.area gdf[floors] (gdf[height] / 3).round().clip(lower1) gdf[gfa] gdf[footprint] * gdf[floors] # 估算总建筑面积 # 生成 500 米渔网 minx, miny, maxx, maxy gdf.total_bounds cell 500 cols int(np.ceil((maxx - minx) / cell)) rows int(np.ceil((maxy - miny) / cell)) from shapely.geometry import box cells [box(minx i*cell, miny j*cell, minx (i1)*cell, miny (j1)*cell) for i in range(cols) for j in range(rows)] grid gpd.GeoDataFrame(geometrycells, crsgdf.crs) grid[cell_area] grid.geometry.area # 空间连接后聚合 joined gpd.sjoin(gdf, grid, howinner, predicateintersects) agg joined.groupby(index_right).agg( total_gfa(gfa, sum), total_footprint(footprint, sum) ).reset_index() grid grid.reset_index().rename(columns{index: index_right}) result grid.merge(agg, onindex_right, howleft).fillna(0) result[far] result[total_gfa] / result[cell_area] result[density] result[total_footprint] / result[cell_area] result[[far, density, geometry]].to_file( beijing_far_density.geojson, driverGeoJSON )这段代码的关键参数是cell500500 米网格在北京这种高密度城市能较好平衡细节和噪声。floors用高度除以 3 米估算如果你有准确楼层字段直接替换。far就是容积率density是建筑密度输出成 GeoJSON 后可以直接在 GIS 里做分级渲染。注意sjoin用inner会丢掉落在网格外的建筑如果全域覆盖完整影响不大如果边界有缺失改用left并处理空值。提示容积率超过 10 的网格要重点检查可能是高度字段异常或建筑重复计算这种极端值在后续分析里会放大误差。我自己处理这类数据的习惯是拿到 shp 先跑一遍字段体检和坐标核对再抽样几个地标验证高度最后才批量处理。400 万栋听起来吓人拆成网格后每块也就几万栋一台普通笔记本就能跑。真正费时间的从来不是计算而是数据清洗和坐标对齐这些脏活。希望帮到你。本文还有配套的精品资源点击获取
返回列表