多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

宁波建筑物及高程shp数据WGS84坐标系使用指南

宁波建筑物及高程shp数据WGS84坐标系使用指南 简介这份资源面向GIS从业者、城市规划与地理信息相关专业的学生提供宁波地区的建筑物与高程矢量数据可直接用于空间分析、地图制作与地形研究。压缩包共8个文件约7.2MB以SHP格式为核心包含shp几何数据、dbf属性表、shx索引、prj投影定义以及xml元数据、dat与key等辅助文件解压后即可在ArcGIS、QGIS等软件中加载查看。数据采用WGS84坐标系能与全球其他同坐标系数据集精确叠加便于开展城市扩张分析、地形变化监测与防洪规划等工作。目前已有673人学习下载适合需要宁波本地建筑轮廓与高程底图、希望快速上手空间分析的初中级用户参考使用。1. 宁波建筑物及高程 shp 数据 wgs84 坐标系这份包到底能干什么做城市三维、内涝模拟或者建筑密度分析的人多半都经历过这样的场景项目刚立项甲方一句“先把宁波的底图数据搞到”然后你打开浏览器搜了半天要么是收费的、要么坐标系对不上、要么只有建筑轮廓没有高程。这份「宁波建筑物及高程 shp 数据 wgs84 坐标系」压缩包解决的就是这个从零到一的问题——它把宁波范围内的建筑物面数据和配套高程信息统一整理成了 shp 格式并且坐标系锁定在 WGS84。这意味着你拿到手之后不需要再折腾投影转换直接丢进 ArcGIS、QGIS 或者 PostGIS 就能开始干活。适合谁做城市 GIS 分析的、搞建筑日照和风环境模拟的、需要快速搭建三维城市场景的以及那些被坐标系折磨过、只想拿一份“能直接用”的数据先把流程跑通的人。下面我按实际拆包和使用的顺序把这份资源从结构、用法到坑点讲清楚。2. 拆开压缩包shp 文件结构、坐标系与高程字段怎么读2.1 shp 不是单个文件别只拷贝 .shp 就以为完事很多人第一次接触 shp 格式会以为它就是一个.shp文件。实际上 ESRI Shapefile 是一组文件的集合少一个都可能导致数据打不开或者属性丢失。这份宁波数据包解压后你至少会看到以下几类文件文件后缀作用缺失后果.shp存储几何形状点、线、面没有几何数据不存在.shx几何索引记录每个要素的偏移量部分软件无法定位要素.dbf属性表存放字段值丢失所有属性只剩空壳.prj投影信息定义坐标系坐标系未知坐标值变成“裸数字”.cpg字符编码说明中文属性可能乱码我一般拿到 shp 包的第一件事是确认这五个文件是否齐全。尤其是.prj它决定了你后面要不要做坐标转换。这份数据标注的是 WGS84那么.prj里应该能看到GCS_WGS_1984或者对应的 EPSG:4326 定义。如果你打开.prj发现是空的或者写的是CGCS2000那就得留个心眼——虽然 WGS84 和 CGCS2000 在多数城市场景下差异不大但严格来说它们不是同一个坐标系做高精度叠加时会有米级偏差。2.2 WGS84 坐标系的含义与常见误用WGS84 全称 World Geodetic System 1984是一种地心坐标系也是 GPS 原始输出的坐标系。它的坐标单位是经纬度经度范围 -180 到 180纬度范围 -90 到 90。宁波大致位于东经 121° 到 122°、北纬 29° 到 30° 之间所以你在属性表或者几何信息里看到的坐标值应该是类似121.55, 29.87这样的小数。这里有一个高频翻车点很多人把 WGS84 的经纬度直接当成平面坐标去算面积和长度。在 ArcGIS 里如果没设置投影直接对 WGS84 的 shp 做“计算几何”得到的面积单位是平方度没有任何实际意义。正确做法是先投影到适合宁波的平面坐标系比如 UTM Zone 51N 或者 CGCS2000 高斯克吕格投影然后再算面积。我一般会在 QGIS 里用“重新投影图层”功能把 WGS84 转成 EPSG:32651UTM 51N这样单位就是米算出来的建筑面积才靠谱。2.3 高程字段藏在哪里怎么判断能不能用建筑物数据带高程通常有两种形式一种是几何本身是三维的每个节点有 Z 值另一种是二维几何加一个属性字段比如height、elevation、H之类的。这份宁波数据我拆开后发现建筑物面是二维的高程信息放在属性表里。你需要打开.dbf或者用 GIS 软件查看字段列表找到类似HEIGHT、ELEV、H_AVG这样的字段。判断高程能不能用要看三点一是字段有没有值二是值的范围是否合理三是单位是米还是层数。宁波市区普通住宅层高大约 2.8 到 3 米如果字段值集中在 3、6、9 这种数字那很可能是层数而不是米需要乘以层高换算。如果字段值在 10 到 100 之间那大概率是米。我见过一些数据把高程写成字符串类型导致排序和计算都出错所以拿到手先用Describe或者字段属性看一眼类型。import shapefile # 需要 pip install pyshp sf shapefile.Reader(ningbo_buildings.shp) fields [f[0] for f in sf.fields[1:]] # 跳过第一个 DeletionFlag print(字段列表:, fields) # 查看前 5 条记录的属性 for i, rec in enumerate(sf.records()[:5]): print(f记录 {i}: {rec})这段代码用pyshp库读取 shp 的字段和记录。sf.fields返回的是字段定义列表第一个元素是DeletionFlag需要跳过。sf.records()返回属性记录每条记录是一个列表顺序和字段列表对应。通过打印前几条你能快速判断高程字段叫什么、值长什么样。如果字段里有中文记得检查.cpg文件里的编码通常是 UTF-8 或者 GBK不对的话用encoding参数指定。3. 把数据用起来从加载、投影到建筑高度提取的完整操作3.1 在 QGIS 里加载并检查数据完整性QGIS 是免费开源的对 shp 支持很好我一般用它做第一轮检查。打开 QGIS直接把.shp文件拖进图层面板如果坐标系正确你会看到建筑物轮廓叠加在底图上。如果位置偏了大概率是.prj缺失或者坐标系定义错误。加载后右键图层选择“属性”然后看“信息”选项卡。这里会显示要素数量、几何类型、坐标系名称。宁波的建筑面数据要素数量应该在几万到几十万之间具体取决于覆盖范围和精细度。如果只有几百个那可能是样本数据或者只覆盖了某个区。几何类型应该是 Polygon 或者 MultiPolygon如果是 Point那这份数据就不是建筑轮廓而是别的什么东西。接下来检查属性表。右键图层选择“打开属性表”看看高程字段有没有空值。空值太多的话后续做三维拉伸会很麻烦。我一般会先统计一下空值比例如果超过 20%就得考虑用其他数据补或者用插值方法填。3.2 用 Python 做批量投影转换和面积计算QGIS 手动操作适合检查但如果你要批量处理多个 shp 或者集成到自动化流程里Python 更合适。下面这段代码用geopandas把 WGS84 的建筑物数据投影到 UTM 51N然后计算每个建筑物的面积并筛选出面积大于 100 平方米的要素。import geopandas as gpd # 读取 shp指定坐标系为 WGS84 gdf gpd.read_file(ningbo_buildings.shp) print(原始坐标系:, gdf.crs) # 如果 crs 为空手动设置 if gdf.crs is None: gdf gdf.set_crs(epsg4326) # 投影到 UTM Zone 51N单位变为米 gdf_utm gdf.to_crs(epsg32651) # 计算面积平方米 gdf_utm[area_m2] gdf_utm.geometry.area # 筛选面积大于 100 平方米的建筑 gdf_filtered gdf_utm[gdf_utm[area_m2] 100] print(f筛选后要素数量: {len(gdf_filtered)}) # 保存结果 gdf_filtered.to_file(ningbo_buildings_utm.shp, encodingutf-8)gpd.read_file读取 shp 后返回一个 GeoDataFramecrs属性就是坐标系。如果.prj文件缺失crs会是None这时候需要手动set_crs。to_crs(epsg32651)是投影转换的核心EPSG:32651 对应 UTM Zone 51N覆盖宁波所在区域。转换后geometry.area计算的就是平方米。筛选面积是为了去掉那些细碎的、可能是数据噪声的小多边形。最后保存时指定encodingutf-8避免中文属性乱码。3.3 提取建筑高度并做简单三维拉伸如果你要做三维可视化比如在 Cesium 或者 Blender 里搭场景需要把二维面拉伸成三维体。前提是你要有一个可靠的高度字段。假设字段名是HEIGHT单位是米下面这段代码用geopandas和matplotlib做一个简单的三维拉伸预览。import geopandas as gpd import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D gdf gpd.read_file(ningbo_buildings_utm.shp) gdf[HEIGHT] gdf[HEIGHT].fillna(10) # 空值用 10 米填充 fig plt.figure(figsize(12, 8)) ax fig.add_subplot(111, projection3d) for idx, row in gdf.iterrows(): x, y row.geometry.exterior.xy height row[HEIGHT] # 绘制底面和顶面轮廓 ax.plot(x, y, zs0, zdirz, colorgray, alpha0.5) ax.plot(x, y, zsheight, zdirz, colorblue, alpha0.5) ax.set_xlabel(X (m)) ax.set_ylabel(Y (m)) ax.set_zlabel(Height (m)) plt.show()这段代码遍历每个建筑面取外轮廓的 x、y 坐标分别在 z0 和 zheight 处画线形成一个简单的三维框架。fillna(10)是处理空值的常见做法但更严谨的方式是用周边建筑的平均高度或者回归模型预测。实际项目中我一般会把高度字段单独导出成 CSV用 pandas 做清洗和插值再合并回去。注意三维拉伸前一定要确认高度字段的单位。如果字段是层数记得乘以层高住宅约 2.8 米商业约 3.5 米。单位搞错整个三维场景的比例就全乱了。4. 避坑与排查坐标系、高程字段和文件编码的五个血泪经验4.1 现象加载后建筑跑到非洲西海岸去了原因.prj文件缺失或者坐标系定义错误GIS 软件默认按 WGS84 的经纬度解释但实际坐标值可能是投影坐标比如高斯克吕格数值很大导致位置完全偏离。解决先看坐标值范围。如果 x 是 6 位数、y 是 7 位数那基本是投影坐标不是经纬度。这时候需要找到正确的投影参数手动定义坐标系。宁波常用的投影是 CGCS2000 高斯克吕格 3 度带中央经线 121°30′。在 QGIS 里可以用“自定义坐标系”输入 PROJ 字符串或者直接问数据提供方要.prj文件。4.2 现象面积算出来是 0.0001 这种离谱数字原因在 WGS84 地理坐标系下直接计算几何单位是平方度不是平方米。解决先投影到平面坐标系再算。EPSG:32651 是 UTM 51N适合宁波或者用 EPSG:4547CGCS2000 3 度带 120E。投影后再算面积数值应该在几十到几千平方米之间符合建筑尺度。4.3 现象高程字段全是空值或者全是 0原因数据采集时没有关联高程信息或者字段被错误映射。解决先确认字段名是否正确有时候高程信息藏在ELEVATION、H、ALT等不同字段里。如果确实没有可以用公开的 DEM 数据比如 SRTM、ASTER提取每个建筑中心点的高程作为地面高程再结合层数估算建筑高度。常见做法是用rasterio读取 DEM然后用geopandas的sample方法采样。4.4 现象中文属性显示成乱码原因.dbf文件的编码和 GIS 软件默认编码不一致。老版本 ArcGIS 默认用 GBKQGIS 默认用 UTF-8。解决用.cpg文件指定编码内容写UTF-8或者GBK。如果没有.cpg可以在 QGIS 的图层属性里手动设置编码或者用 Python 读取时指定encoding参数。我一般会在数据交付前统一转成 UTF-8避免跨平台问题。4.5 现象建筑面之间有大量重叠和缝隙原因数据来源可能是多个部门拼接的或者从 CAD 转换时没有做拓扑处理。解决用 QGIS 的“拓扑检查器”或者geopandas的overlay做去重和融合。对于重叠可以用unary_union合并对于缝隙如果影响不大可以忽略如果要做精确分析需要用“消除”或者“融合”工具处理。我一般会先做一次buffer(0)清除自相交然后再做后续操作。5. 进阶技巧用高程数据做建筑高度反演与三维瓦片生成5.1 从 DEM 和建筑面反演建筑高度如果你拿到的数据里高程字段不可靠或者你想验证现有高度字段的准确性可以用 DEM 做反演。思路很简单建筑屋顶的高程减去地面高程就是建筑高度。地面高程可以用建筑面周边的 DEM 值来估计。import geopandas as gpd import rasterio from rasterio.mask import mask import numpy as np # 读取建筑面和 DEM gdf gpd.read_file(ningbo_buildings_utm.shp) dem rasterio.open(ningbo_dem.tif) heights [] for idx, row in gdf.iterrows(): geom [row.geometry.__geo_interface__] try: out_image, out_transform mask(dem, geom, cropTrue) # 取建筑范围内的 DEM 最大值作为屋顶高程最小值作为地面高程 roof_elev np.max(out_image) ground_elev np.min(out_image) height roof_elev - ground_elev heights.append(height) except Exception as e: heights.append(np.nan) gdf[height_from_dem] heights gdf.to_file(ningbo_buildings_height.shp, encodingutf-8)这段代码用rasterio.mask按建筑面裁剪 DEM然后取最大值和最小值做差。mask函数返回裁剪后的影像和新的变换矩阵。np.max和np.min分别对应屋顶和地面。实际使用中DEM 分辨率越高反演越准。如果 DEM 是 30 米分辨率的 SRTM那对于小建筑来说误差会比较大只适合做粗略估算。5.2 把 shp 转成 3D Tiles 的预处理要点现在很多三维平台比如 Cesium用 3D Tiles 格式。shp 不能直接转 3D Tiles需要先转成带高度的 GeoJSON 或者 OBJ再用工具转换。常见流程是shp → 投影 → 提取高度 → 生成 GeoJSON → 用3d-tiles-tools或者Cesium ion转换。我一般会先用geopandas把数据转成 GeoJSON确保坐标系是 WGS84因为 3D Tiles 通常用 WGS84然后写一个脚本把每个建筑面拉伸成体导出成 OBJ。这个过程比较繁琐但好处是可控。如果数据量不大也可以用 Blender 的 GIS 插件直接导入 shp 做拉伸然后导出 glTF。提示转 3D Tiles 时建筑高度字段一定要确认单位是米。如果原始数据是层数先换算成米再导出。另外WGS84 的经纬度坐标在三维引擎里需要转成笛卡尔坐标Cesium 会自动处理但你要确保数据本身没有投影偏移。5.3 一个我常用的验证习惯每次拿到新的 shp 数据我都会做三件事第一用 QGIS 加载并叠加在线底图看位置对不对第二打开属性表看字段类型和空值比例第三用 Python 算一下投影后的面积和周长看数值是否合理。这三步走完基本能判断这份数据能不能直接用、需不需要预处理。从那以后我每次拿到 shp 都强制走一遍这个流程省得后面返工。希望帮到你。本文还有配套的精品资源点击获取
返回列表