ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

2020上海建筑面shp数据解析:面积、人口与坐标系处理

2026/10/3 3:36:44 拓冰建站 浏览量
2020上海建筑面shp数据解析:面积、人口与坐标系处理 简介这份数据包提供2020年上海建筑物面矢量数据含城市与农村建筑轮廓、面积及人口关联信息主要用于城市内涝治理、下垫面分析、建筑能源建模与城乡规划评估适合GIS开发者、规划师及SWMM建模人员作为底图或空间分析输入。包体共6个文件以shp主文件为核心配套shx几何索引、dbf属性表、prj坐标系定义、cpg字符编码及xml元数据压缩后总大小约108.78MB结构精简可直接加载到ArcGIS或QGIS中查看。目前已有621人学习下载说明该数据在相关领域受到一定关注。借助这份建筑面要素及其属性字段可快速提取建筑轮廓、统计面积并叠加人口信息为内涝风险评价、屋顶可用性评估或城市更新研究提供较可靠的数据支撑减少自行矢量化或数据清洗的时间投入。1. 这份2020上海建筑面数据到底能拿来做什么2020年做城市更新调研的时候我拿到过一批类似的数据shp格式的上海建筑物面图层每栋楼的面要素都带属性属性里有面积有人口。对于做GIS分析和规划的人来说这意味着不用再手动逐栋描轮廓也不用去翻街道统计年鉴直接在地图上就能回答这栋楼有多少人、那个片区建筑面积多少这类问题。这份数据对规划设计师、城市数据分析工程师和做社区治理的人来说特别实用尤其是需要把人口落到空间单元上的场景。shp格式本身是GIS里的通用交换格式ArcGIS、QGIS、PostGIS、GeoPandas都能直接读这是它能广泛流转的基础。不过拿到手能不能直接用关键看面积字段和人口字段的口径、坐标系是否被正确理解这正是下面几章要拆开讲的事。2. 拆开shp格式看门道图层字段、坐标系与面积人口信息怎么组织2.1 shapefile不止一个文件.shp/.dbf/.prj是怎么配合的很多刚接触这个数据的人拿到一个xxx.shp就拖进软件却不知道shp格式其实是由一组文件共同构成一个图层。至少要有 .shp 存几何形状、.shx 存几何索引、.dbf 存属性表外加 .prj 描述坐标系。如果再有 .cpg 文件那是记录属性表字符编码的。拷给别人时如果只发了 .shp很可能打开后只有图形没有属性或者直接提示文件不完整。所以拿到数据后的第一个动作不是急着打开而是先看目录里有没有 .prj。没有 .prj 的 shp 在 ArcGIS 或 QGIS 里按未知坐标系处理后面叠加上海行政区边界大概率错位。另一个容易被忽视的是 .cpg 文件它决定了 dbf 里的中文能不能正常显示。2020年上海建筑面数据如果来自国内测绘单位属性表编码常见是 GBK 或 GB18030而很多 GIS 软件默认读 UTF-8乱码往往就是这么来的。属性表里常见的字段设计是这样的一个唯一标识字段比如 OBJECTID 或 FID类型是整型一个面积字段可能会叫 Area、Shape_Area、BULID_AREA 之类的双精度浮点型一个人口字段常见命名有 Pop、POPULATION、人口数类型可能是整型或浮点型还可能有一个建筑用途或类型字段比如住宅、商业、办公、工业。不同批次的生产单位字段命名习惯差异很大有的用英文有的用拼音缩写有的直接用中文。不要假设字段一定叫某个名字打开后先看前几行属性再动手。2.2 建筑面要素的几何与属性每一条记录对应什么shp 里的建筑面要素几何类型一般是 Polygon 或 MultiPolygon。一栋独立的矩形建筑是一个 Polygon如果有天井或内院可能是带内环的 Polygon如果一栋楼由裙房和塔楼组成但中间有连廊可能会被生产方画成 MultiPolygon。统计时要注意一条记录不一定等于一栋建筑也可能是建筑群。从数据生产的视角看2020年上海建筑面数据多半是基于高分影像或既有地形图按建(构)筑物主体轮廓来勾绘的。这里的面通常只覆盖主体建筑不包含附属的围墙、空地。如果你要做的是容积率或建筑密度分析这个面就是要分母但如果是计算人均居住用地则还需要拿到小区范围或街坊边界不能只用建筑面。每个面要素的属性表行数和几何是一一对应的。检查数据质量时先看一眼几何类型里有没有 MultiPolygon 混进来量多的话说明源数据可能有合并或切割操作。用 GeoPandas 读取后可以快速把所有 MultiPolygon 过滤出来人工抽查几处看是不是误操作导致一个面包含了两栋不相连的楼。2.3 面积字段与人口字段单位、口径和易混点先说面积这是最容易翻车的地方。shp 里的面积字段有两种来源一种是面要素几何计算出来的地理面积另一种是采集时从房产信息或竣工图纸录入的建筑面积。两者的口径完全不同——地理面积是轮廓范围内的投影平面面积建筑面积可能包含阳台、地下室或外墙保温层数值差异超过 5% 很常见。想确认面积字段的合理性最直接的办法是在软件里重新算一次几何面积。QGIS 里可以用字段计算器输入$areaArcGIS 里用Calculate GeometryPython 里用 GeoPandas 的geometry.area。如果算出来的值和属性字段差很多就得搞清楚字段单位是平方米还是公顷。上海本地数据有的习惯用万平方米做单位属性表里写着 1.23实际是 12300 平方米。这个坑后面再展开。人口字段更复杂。标题里写了包含人口信息但人口这个词可以被解释成好几种口径。2020年是第七次全国人口普查年份所以这份数据里的人口很可能是从普查数据按街镇为单位再结合建筑性质和建筑面积分摊到每一栋建筑的。这里有三个可能性一是常住人口指普查时点实际居住在这个建筑里的人二是户籍人口可能不包含外来常住人口三是建筑容量按建筑类型和层数估算最多能住多少人。三者数值会差出好几倍。没有元数据时怎么判断口径一个常用办法是把全区建筑人口字段加总和第七次人口普查的全区常住人口对比。如果总和与普查数相差在 20% 左右大概是按普查人口分摊的如果明显偏大比如是普查数的三倍那很可能是把整栋楼所有房间按每个人都空间化了或者把户数当成了人数。这个判断直接影响后续所有分析结论宁可多花十分钟校验也不要拿一个不明口径的人口直接做设施配置。2.4 坐标系与投影看到EPSG编码先别急着算面积坐标系决定了这份 shp 到底摆在地球哪个位置也决定了面积算出来是什么单位。打开文件的 .prj常见的可能是 CGCS2000 地理坐标系、WGS84 地理坐标系或者带投影的平面坐标系。如果是地理坐标系坐标值约等于经纬度上海的范围大约在 121.4, 31.2 附近如果是投影坐标系坐标值会是七八位的米制数比如 12140000, 3400000 这种说明可能是高斯克吕格投影或地方坐标系。这里有一个关键判断上海建筑面数据的坐标如果在 121 和 31 附近说明是经纬度不能直接算面积因为经纬度下的面积单位是平方度没有任何现实意义。必须先投影到平面坐标系通常建议投影到适合上海地区的 CGCS2000 高斯克吕格投影或 UTM 51N。UTM 51N 中央经线为 123°E上海在东经 121.5° 左右偏离中央经线约 1.5 度面积变形很小适合做统计和展示。另一个容易被忽视的问题是 CGCS2000 和 WGS84 之间的差异。两者在空间位置上通常只有几厘米到几米的差别对建筑面数据叠加来说通常够用。但如果源数据里有的图层是 CGCS2000有的是 WGS84直接叠加会在上海中心城区产生米级位移别把它完全等同于误差要统一坐标系后再做空间分析。下面用一个简表整理常见字段和注意点方便你在打开属性表时对照。常见字段类型意义易错点OBJECTID / FID整型唯一标识不能当作人口或面积用Area / Shape_Area浮点型面要素面积单位可能是平方米、公顷或万平方米Pop / 人口浮点型或整型人口数量可能是常住人口、户籍人口或建筑容量Type / 用途字符串建筑类型与人口字段结合使用时要注意类型权重楼层整型建筑层数可用于校验人口规模是否合理3. 用Python把shp数据跑起来读取、清洗与按区统计面积和人口3.1 环境准备与读取shp的最小代码Python 侧处理 shp 的主流方案是 GeoPandas。它底层依赖 GDAL安装的时候顺手装好 pyproj 和 shapely 就行。如果你用的是 Anaconda常见做法是创建一个虚拟环境然后conda install geopandas避免二进制包的依赖冲突。读取 shp 的最小代码很简单import geopandas as gpd # 读取shp注意指定编码避免中文乱码 gdf gpd.read_file(shanghai_buildings_2020.shp, encodingutf-8) # 打印行数、列名和前面几行先摸清数据长什么样 print(gdf.shape) print(gdf.columns.tolist()) print(gdf.head(3))这段代码里有两个关键参数。第一是encoding如果打开后字段名或字段值是乱码把utf-8换成gbk或gb18030大部分国内生产的 shp 都用这个编码。第二是文件路径建议把 shp 的所有配套文件放在同一个目录路径里尽量别带中文否则某些依赖库会读不出来。读取完成后先别急着算打印gdf.crs看一眼坐标系。如果crs显示为 None说明这个 shp 缺少 .prj 文件后续做投影转换会报错需要先根据数据范围人工判断并指定坐标系。3.2 面积字段的校验用几何重新计算并对比拿到数据后最值得做的一件事是用几何面积去校验属性表里的面积字段。下面这段代码把数据投影到米制坐标系然后重新计算每个要素的面积# 投影到平面坐标系这里以UTM 51N为例单位为米 gdf_metric gdf.to_crs(epsg32651) # 重新计算几何面积结果放在新列里 gdf_metric[geom_area] gdf_metric.geometry.area # 如果属性表里已经有面积字段做一次对比 if Area in gdf_metric.columns: gdf_metric[area_diff] gdf_metric[geom_area] - gdf_metric[Area] print(gdf_metric[area_diff].describe())这里epsg32651是 UTM 51N 的 EPSG 编号适用于上海这种经度范围。to_crs会把经纬度坐标转换成以米为单位的平面坐标这样geometry.area算出来的单位就是平方米。如果数据源本身就是平面坐标系可以跳过to_crs但最好确认一下源投影的单位是不是米。area_diff这一列能帮你快速发现口径问题。如果绝大部分差值为 0说明属性表面积就是几何面积可以直接用。如果差值是整体成比例偏移比如Area是geom_area的两倍那很可能属性面积单位是公顷而几何面积是平方米。一定要在分析前把单位统一成平方米不然后面所有计算都会带着一个倍数错误。3.3 人口字段的类型与缺失值处理人口字段经常被读成字符串尤其当原始 dbf 里存的是带千位分隔符的数字或者混入了暂无这类非数字文本。直接求和会报错或者得到一个荒谬的数。下面这段代码先自动查找人口字段再强制转数值import pandas as pd # 用关键词找出人口字段适配 pop / Pop / 人口 等常见命名 pop_cols [c for c in gdf_metric.columns if pop in c.lower() or 人口 in str(c)] print(人口字段候选, pop_cols) # 把候选字段转成数值无法转的变成NaN for col in pop_cols: gdf_metric[col] pd.to_numeric(gdf_metric[col], errorscoerce) # 看一眼每列有多少缺失值 print(gdf_metric[pop_cols].isna().sum())pd.to_numeric的errorscoerce参数会把2,158这类字符串中的逗号视为错误然后转成 NaN。如果你确认数据里的逗号是千分位分隔符需要先str.replace(,, )再去转换。缺失值怎么处理取决于人口字段的口径如果缺失的建筑本来就是无人居住的危房或车库NaN 可以直接按 0 处理如果只是一条漏采的记录按 0 处理会低估总人口。我一般的做法是先看缺失比例少于 2% 就按 0 填充并记录在文档里高于 5% 就得回到原始数据源追查原因。3.4 按区或街镇聚合面积和人口怎么汇总有了清洗后的数据最常见的需求是按行政区汇总建筑面积和总人口。如果属性表里有区级或街镇级字段可以用dissolve一步完成几何合并和属性求和# 假设行政区字段叫 district人口字段是 pop # 先检查字段存在 if district in gdf_metric.columns: agg gdf_metric.dissolve(bydistrict, aggfunc{ geom_area: sum, # 汇总建筑占地面积 pop: sum # 汇总人口 }) # 计算人口密度人/平方公里 agg[pop_density] agg[pop] / (agg[geom_area] / 1e6) print(agg[[geom_area, pop, pop_density]].sort_values(pop, ascendingFalse))dissolve有两个关键参数by指定分组字段aggfunc是一个字典键是要聚合的属性列值是聚合方式。注意dissolve会把同一组内的所有面要素合并成一个 MultiPolygon所以geom_area求和其实是在合并后的几何上求面积。如果原本有重叠的建筑面合并后面积会比各建筑面积总和少这时必须回到第 4 章说的拓扑问题去处理。如果只是想要一个统计表不需要合并后的几何改用groupby更轻量summary gdf_metric.groupby(district).agg( total_geom_area(geom_area, sum), total_pop(pop, sum) ).reset_index()这两种写法结果类似但dissolve会得到一个 GeoDataFrame可以直接用来出图groupby返回普通 DataFrame适合导出 Excel 报表。按需选择即可。3.5 把处理结果保存成新的shp或GeoJSON分析完成后通常要输出结果。保存成 shp 时注意编码问题建议显式指定 UTF-8不然在别的软件里又会乱码。代码很简单# 保存处理后的结果保留别的软件能读懂的坐标系 gdf_metric.to_file(shanghai_buildings_2020_processed.shp, encodingutf-8)保存时 GeoPandas 会自动把坐标系的 .prj 写出来。如果你后续要放进数据库也可以直接存成 GeoJSON 或 PostGIS table但 shp 格式仍然是通用性最好的交换格式。保存后重新读一次确认字段和坐标都没丢这一套流程才算闭环。4. 处理2020上海建筑面数据最容易翻车的五个坑4.1 属性表中文乱码现象、原因、解决现象在 QGIS 或 ArcGIS 里打开 shp字段名显示成浣?..或者字段值里的中文变成一堆乱码建筑名称、行政区名全都不可读。原因shp 的属性表存放在 .dbf 文件里里面的字符编码是生产方写入的。国内常见 GBK 或 GB18030而 ArcGIS 桌面版新版默认按 UTF-8 读QGIS 也可能因为缺少 .cpg 文件而猜错编码于是乱码。解决先看同目录下有没有 .cpg 文件它记录了原始编码有它一般不会乱。没有的话在 QGIS 中加载 shp 时选择数据源编码为 GB18030 或 GBKGeoPandas 则用encodinggbk读取。乱码修复后建议另存一份 UTF-8 编码的 shp用gdf.to_file(..., encodingutf-8)写出去这样后续分享给同事不会再触发同一问题。4.2 面积字段和几何面积差好几倍现象在软件里选一个建筑属性表里面积写的是 2000用几何计算工具量出来却是 20000或者反过来。原因绝大多数时候是单位问题。有的数据源把面积字段单位写成公顷数值是2但几何计算结果是平方米20000有的数据源把面积字段直接从 CAD 里导入单位是平方毫米或平方英尺和米制差得更离谱。还有一种是源数据在投影坐标系下生产属性面积是投影面积但后期把数据重新转成了经纬度 WGS84几何没跟着重算导致直接量出来的面积和字段值对不上。解决不看字段名只看数值量级。一个正常的建筑面积通常在几十到几万平方米之间。先用第 3 章的代码算geom_area再和属性字段对比如果成固定倍数把这个倍数找出来统一换算成平方米。如果差值不规则那就别用属性面积统一用几何面积作为分析基准。4.3 人口信息不是建筑实有人口而是空间化估算值现象某栋建筑属性表里的人口是 23.67 这种小数按街道汇总后发现总人口比该街道统计年鉴人口多出三倍或者反过来少得离谱。原因人口很难精确定位到每一栋楼所以 2020 年这份数据中的人口极可能是一种空间化结果。常见算法是把街镇级人口总量按建筑面积比例或建筑类型权重分摊到每个建筑面上。它做的是空间分布模拟不是调查统计。如果我们把它当真实楼栋人口用误差会非常大。解决先做总量校验。把全区人口字段求和和《上海市统计年鉴》里的常住人口对比。如果总量基本吻合说明分摊口径是对的可以用于分析人口空间分布趋势如果总量差太多检查是不是把户数字段当成了人口很多数据源会同时给户数和人口两个字段户数乘以户均人数才能得到人口。不管哪种情况报告中都要注明人口为模型估算值。4.4 坐标系搞错叠加到上海地图上跑到海上现象把建筑面数据和上海行政区边界叠加建筑点全部出现在长江口或杭州湾里离真实位置偏差几十公里甚至更远。原因一份 shp 可能被反复转换过坐标系。最常见的情况是 .prj 文件写的是 CGCS2000但实际坐标是从 WGS84 地理坐标系转过来的或者 .prj 文件丢失软件按默认的 EPSG:4326 读取了本来是投影坐标的数据直接把八位数的坐标当成经纬度解析。解决打开前先看gdf.crs再用坐标范围判断。如果显示的坐标值在 121.4/31.2 附近说明是经纬度如果在 12100000/3100000 附近说明是投影坐标。两者不能直接叠。确定源坐标系后统一转换到目标坐标系。我习惯先to_crs(epsg32651)做一个标准化的米制投影再和同投影的行政边界叠加。如果是缺少 .prj 的文件只能通过边界匹配去反推坐标系这属于高阶操作新手建议直接找数据提供方要坐标说明。4.5 建筑面要素拓扑重叠导致人口重复计入现象按区汇总pop后人口总量比预期多出 30% 以上或者用建筑面和道路缓冲区做叠加时很多建筑被重复选中。原因建筑面数据在生产过程中可能有重叠。一种情况是同一栋建筑被重复数字化了两遍另一种情况是相邻建筑轮廓线没有严格吸附边界交叉形成了微小重叠区。因为 shp 允许面要素互相重叠软件不会提示错误所以统计求和时这些重叠部分的人口被加了两次。解决先检查重叠。用gdf.geometry.is_valid看几何是否合法再用gdf.geometry.unary_union和原始图层做 overlay找出有重叠的部分。修复可以用shapely的make_valid函数先把非法几何修正再对重叠的相邻建筑做去重处理。如果只是做按区汇总更稳妥的办法是先按唯一建筑编号groupby合并多面要素确保一条记录对应一栋建筑后再聚合计人口。实际操作中我遇到的大多数重叠来自数据拼接按唯一 ID 去重能解决大部分问题。5. 把数据用活按街道计算人口密度并验证到能看5.1 正确计算人口密度分母不是建筑覆盖面积拿到人口和面积后很多人顺手就用总人口除以总建筑占地面积得到一个建筑底面积密度。这个值只能反映建筑底部的拥挤程度不是居住密度。真正有规划意义的是人口密度除以街道土地面积或者建设用地面积。计算时先把建筑点人口按街镇汇总再和 2020 年上海街镇行政区划 shp 做空间关联。街镇边界 shp 从公开的天地图或规划数据平台下载属性表里通常有行政区代码和名称。用行政代码字段做 join避免同名的街镇错配。最后用街道总人口 / 街道总面积单位用人每平方公里。5.2 画一张可验证的图下面这段代码基于第 3 章处理好的agg结果做可视化并按人口密度排序看前五个街道import matplotlib.pyplot as plt # 设置图形大小 fig, ax plt.subplots(1, 1, figsize(10, 12)) # 按人口密度填充颜色 agg.plot(columnpop_density, cmapOrRd, legendTrue, axax, edgecolorgrey, linewidth0.3) ax.set_title(2020 Shanghai Population Density by Street (person/km2)) plt.tight_layout() plt.show()出图后做两件事。第一是看空间分布是否合理核心城区人口密度应该显著高于郊野黄浦、静安的老城厢密度高松江新城、青浦新城有局部峰值。第二是对比值上海市中心城区街镇人口密度通常在 1.5 万到 5 万人每平方公里远郊街镇可能只有 2000 到 8000。如果某一个街镇超过 10 万大概率是人口字段里有异常值回到那条街道的建筑记录里去查。5.3 一个必须养成的数据习惯真正的验证不只是画一张图而是把数据结果和独立来源对齐。我现在的习惯是任何一份带人口的建筑 shp到了我手里第一步一定是看 .prj第二步是算几何面积和属性面积的比值第三步才是做任何聚合。这三步做完这份数据能不能用、哪里能用到什么粒度心里基本有数。早年我拿到过一份看起来非常完美的建筑面数据字段齐全、坐标正确我直接用属性面积做了容积率统计后来偶然发现面积单位是公顷所有结果整整差了一万倍。那个上午我重做了三天的工作量。从那以后每一步计算之前都强制自己回答一个问题这个字段的单位是什么这个字段的口径是什么回答不上来就算出来的数字再好看也宁可先不做。2020年上海建筑面数据的价值在于它把建筑、面积、人口三个维度放进了一个 shp 里但这份价值是建立在正确理解字段口径之上的。把坐标系、单位、拓扑这三关过掉它就能成为一个可靠的空间分析底图。希望上面这些踩过的坑和替换方案能在你做同样的数据处理时帮你省下一些时间。本文还有配套的精品资源点击获取