ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

2022西安建筑轮廓GIS数据:从坐标统一到白模批量生成实战

2022西安建筑轮廓GIS数据:从坐标统一到白模批量生成实战 简介2022年西安建筑轮廓GIS数据是一份面向城市规划、建筑设计与环境研究等领域的矢量地理数据包详细记录了西安市建筑物的边界、形状、高度及用途等关键属性可支撑空间分布分析、城市形态研究与可视化表达。资源压缩包共6个文件核心为shp建筑几何轮廓、dbf属性信息、shx几何索引辅以prj投影坐标、cpg及xml元数据文件整体约62.33MB。目前已有301人下载学习适合GIS初学者、规划从业者及科研人员使用。借助这份数据不仅可快速在ArcGIS、QGIS等平台生成西安建筑轮廓地图进行建筑密度、高度分异与街区形态分析还能支撑光照模拟、风环境评估、热岛效应研究以及交通布局优化为历史城区保护与智慧城市相关课题提供量化依据。1. 2022年西安建筑轮廓GIS数据先把它当数据集而不是图纸一份名为“2022年西安建筑轮廓GIS数据”的资料本质上是带空间坐标的多边形集合而不是 CAD 图纸里的闭合多段线。建筑轮廓在 GIS 里的价值在于每一栋楼的边线都能挂楼号、层数、结构类型能和道路、影像、地块相互叠加做空间统计。多数工程师第一次拿到这类数据时最容易犯的错是直接拖进 GIS 开始画完全忽略坐标系和几何有效性等后期做白模或批量出图时才发现整层数据偏移几十米。处理这份数据我习惯按固定顺序来先统一坐标再做坐标成面然后拓扑修复接着补高度属性最后才谈三维展示和批量出图。这篇文章的目的是把这条链路上最容易被卡住的参数和判断标准写清楚适合做城市白模、街区底图、建筑尺度空间分析的人参考。2. 拿到2022年西安建筑轮廓数据后的第一步统一坐标再做坐标成面2.1 先确认坐标参考系不要把坐标当属性处理我拿到一份建筑轮廓数据第一件事不是打开图层看形状而是找坐标参考系。Shapefile 就看同名.prj文件GeoJSON 就看crs说明没有元数据时也要从数据生产年份和区域反推。2022 年西安范围的建筑轮廓常见三种坐标情况坐标参考系EPSG 编号典型场景WGS84 经纬度EPSG:4326预览、Web 前端、GeoJSON 交换CGCS2000 地理坐标EPSG:4490国内成果库存储、原始工程数据WGS84 / UTM zone 49NEPSG:32649米制距离、面积计算、三维拉伸如果原始数据是西安地方独立坐标系那上面三个都不直接用。这时需要从数据说明里找中央经线、椭球参数和偏移量至少把平面坐标转成 CGCS2000 再进工作流。我不建议在没确认坐标的情况下直接点“另存为 WGS84”因为投影转换参数不对时轮廓不会报错只会整体漂移这种错误最难排查。2.2 用 ogr2ogr 统一转成带 UTF-8 的 GeoJSON无论是 Shapefile 还是 File GDBGDAL 的ogr2ogr是最稳妥的格式转换方式。对于这份 2022 年西安建筑轮廓数据我一般先把它统一成 EPSG:4490 的 GeoJSONogr2ogr -f GeoJSON xian_2022_buildings_4490.geojson xian_2022_buildings.shp -t_srs EPSG:4490 -lco ENCODINGUTF-8参数说明-t_srs是强制输出坐标到 EPSG:4490保证后续所有图层空间基准一致-lco ENCODINGUTF-8避免中文字段在 GeoJSON 里乱码。若原始 Shapefile 缺少.prj文件GDAL 会默认当成 EPSG:4326此时要先加-a_srs指定原始坐标例如-a_srs EPSG:4490再执行转换。转换完成后把结果和 OpenStreetMap 的路网或影像叠加一次。如果建筑轮廓与路网关系不合理优先回头检查坐标源头而不是手工移动图层。这里有个容易被忽略的细节2022 年西安建筑轮廓如果来自不同行政区的拼接成果可能每个区用了不同的采集基准最典型的特征就是区界两侧建筑错位。遇到这种情况要在 GIS 里用“矢量-几何-移动”逐个区域对齐而不是全局转换一次。2.3 只有点坐标表时用“坐标成面”的工作流有些数据源给的不是现成的面而是一张 CAD 提取出来的坐标表每行是一个转折点带顺序号。GIS 里的做法叫“坐标成面”先把点按顺序连成线再把线转成面。在 QGIS 中处理工具箱里依次运行“点排序”“点转路径”“线转面”三个工具。点排序的关键字段是坐标表中的顺序号没有顺序号时用方位角排序否则同一栋楼的点会连成锯齿线。线转面之前要先检查线层是否闭合未闭合的线转出来的是空面。当点量很大时我习惯直接用 Python 生成 GeoJSON因为可控性更好。下面是一栋建筑由四个角点组成面的最小示例import json pts [ (108.9500, 34.2640), (108.9505, 34.2640), (108.9505, 34.2645), (108.9500, 34.2645), ] ring pts [pts[0]] feature { type: Feature, geometry: { type: Polygon, coordinates: [ring] }, properties: { name: 示例建筑, source: 2022_survey } } print(json.dumps(feature, ensure_asciiFalse))这段代码的逻辑是把点序列首尾相连形成闭环再包一层数组作为 Polygon 的坐标。需要注意三件事第一ring必须闭合否则生成的不是有效面第二点的顺序必须是顺时针或逆时针绕建筑一圈如果点位是按行号蛇形排列生成的面会发生自折叠第三坐标顺序是经度在前、纬度在后写反了会跑到非洲西海岸线上。对成百栋建筑可以用相同的循环处理最后用geopandas.GeoDataFrame统一写文件。2.4 判断这份数据是否值得直接用的检查点拿到统一坐标后的轮廓面不急着清洗先花五分钟看属性表。2022 年西安建筑轮廓数据一般应包含建筑编号、名称、层数、高度、结构类型、采集时间这些字段。如果属性表只有几何没有字段后续做三维拉伸和统计会非常被动。对 2022 年这样较新的数据还应确认是否有“竣工年份”或“采集年份”属性。如果没有就不能把“现状建筑”和“已拆建筑”混在一起当成同一期现状。3. 轮廓数据的清洗不能跳过自相交、重叠与拓扑修复3.1 建筑轮廓常见的三类几何错误拿到面的格式不等于几何有效。2022 年西安建筑轮廓数据如果经过多次转换、叠加或局部编辑极易出现三类问题错误类别现象对后续的影响面自相交边界像绳子打结内部出现交叉面积计算错误白模挤出后产生穿插面重复/重叠面同一栋楼存在两个面积相近的面统计楼栋数翻倍出图时颜色叠加变深细缝与空隙相邻建筑之间出现微小缺口叠加分析时产生错误空洞这三类问题里自相交最难靠肉眼发现因为视觉上往往只是一条边画得有点怪。最可靠的方式不是靠眼睛而是靠几何校验函数。3.2 用 PostGIS 做几何质量检查和批量修复如果数据量在几十万条以上我会先把 GeoJSON 导入 PostGIS。导入后第一件事就是找无效几何SELECT gid, ST_IsValidReason(geom) AS reason FROM xian_2022_buildings WHERE NOT ST_IsValid(geom);ST_IsValidReason会返回“Self-intersection”“Ring Self-intersection”等具体原因直接指出问题发生的环节。定位到问题要素后在编辑层面对单条几何手动修正比全表统一修复更安全因为ST_MakeValid对复杂自相交面的处理结果往往不符合实际建筑边界。查重复和重叠是第二步SELECT a.gid AS a_id, b.gid AS b_id FROM xian_2022_buildings a JOIN xian_2022_buildings b ON ST_Overlaps(a.geom, b.geom) WHERE a.gid b.gid LIMIT 200;ST_Overlaps比ST_Intersects更严格它只返回那些部分重叠但没有互相包含的面适合筛选同一栋楼重复采集的情况。如果两条记录空间重叠且属性字段中的建筑编号相同保留面积较大的、日期最新的那条。若建筑编号不同但重叠率超过 80%则说明原始矢量化时把同一栋楼拆成了两块这种情况要用ST_Union合并再按外墙重新切分。对于单纯的自相交几何修复语句可以写成CREATE TABLE buildings_fixed AS SELECT gid, name, ST_MakeValid(geom) AS geom FROM xian_2022_buildings WHERE NOT ST_IsValid(geom);需要提醒的是ST_MakeValid的输出不一定是 Polygon它可能返回 MultiPolygon 或 GeometryCollection。如果后续要生成白模必须把非 Polygon 类型拆成单面处理否则挤出时会出现空心体。3.3 在 QGIS 里修拓扑以及复制粘贴失败的真正原因不走 PostGIS 时QGIS 的 Topology Checker 插件够用。规则里打开“不能重叠”“不能有缝隙”容差设置为 0.001 米。这里容差单位由图层坐标决定如果用经纬度图层0.001 度覆盖范围太大会把正常相邻建筑全标记为重叠。所以拓扑检查前必须先把图层转到米制投影例如 EPSG:32649。批量修复用 QGIS 工具栏的“修复几何”工具位置在“处理工具箱 → 矢量几何 → 修复几何”。它会调用 GEOS 库做类似ST_MakeValid的处理。如果拓扑检查发现大量要素重叠最好先按建筑编号分组再用“合并同一 ID 的面”处理而不是盲目运行修复工具。这里插一个高频问题很多人在 QGIS 里从检查结果图层复制要素粘贴到原建筑图层时报错。原因有三个按概率排序原图层没有开启编辑状态剪贴板里的几何类型是 MultiPolygon而目标图层被限制为 Polygon两个图层坐标参考系不同QGIS 拒绝自动转换。解决方法是先启动编辑再用“复制要素-粘贴要素”若仍失败就把目标图层的几何类型改为“任何几何类型”复制完再改回去。这个操作在建筑轮廓修图时几乎每周都会碰到。清洗完成后的数据应该满足所有面通过ST_IsValid校验建筑间无重叠相邻建筑之间的缝隙小于等于 0.1 米。这些条件会在后续三维拉伸和批量出图时省掉大量返工。4. 从2022年西安建筑轮廓GIS数据生成2.5D白模高度字段与批量拉伸4.1 先给每个面补高度和楼层字段建筑轮廓本身是二维面想做白模必须有第三维信息。2022 年西安建筑轮廓数据通常带“层数”字段没有“高度”字段时我一般用字段计算器生成建筑高度估算值coalesce(floors, 1) * 3.2coalesce会把空楼层数当作 1避免出现高度 0 的建筑。但这个算法只适用于住宅和普通公建对商铺、厂房、体育馆不适用。更稳妥的做法是按建筑用途分区间赋值建筑类型层高标准住宅2.9 – 3.2 米办公/商场3.6 – 4.5 米工业厂房5.0 – 8.0 米体育/展览8.0 米以上如果数据里只有高度没有层数反向用round(height / 3.2)得到层数但在统计居住人口时不要直接用这个层数因为底商、地下室都不在该计算范围内。4.2 在 QGIS 里快速查看 2.5D 体块不写代码也能看白模效果图层右键 → 属性 → 符号化类型选择“2.5D 渲染”角度设为 45 度高度字段选height或floors。这里必须注意2.5D 渲染的高度的单位是米因此图层 CRS 必须是米制投影。如果还在 EPSG:4326 下操作高度值会被当成经纬度单位整个体块明显变形。要有可交互的 Web 展示用 qgis2threejs 插件。在插件面板里选择“场景-建筑物”把高度属性设为height导出 HTML 即可。qgis2threejs 对中等体量数据表现不错但几千栋楼同时加载会比较卡属于“够用但不适合生产发布”的方案。4.3 用 PostGIS 批量生成三维体块对于需要存储和进一步分析的白模我会在 PostGIS 里启用 SFCGAL 扩展然后批量生成体块CREATE EXTENSION IF NOT EXISTS postgis_sfcgal; SELECT gid, ST_Extrude(geom, 0, 0, height) AS geom_3d FROM buildings_fixed WHERE height IS NOT NULL;ST_Extrude的第二个和第三个参数分别是 X、Y 方向偏移量都设为 0表示只在 Z 轴方向拉伸。生成的几何对象是 PolyhedralSurface导出到 glTF 或 OBJ 时需要先转换为 TIN 或多边形网格。这个方案适合管网撞击分析、视线遮挡分析但不适合直接用 Cesium 加载因为 Cesium 更希望拿到轻量的切片数据。4.4 直接用 CesiumJS 加载轮廓并拉伸Web 可视化最常见的做法是在 CesiumJS 里读 GeoJSON然后用extrudedHeight把建筑拉伸成体块。下面的代码可以直接在浏览器里跑fetch(xian_2022_buildings_4490.geojson) .then(response response.json()) .then(geojson { const viewer new Cesium.Viewer(cesiumContainer, { terrainProvider: Cesium.createWorldTerrain() }); geojson.features.forEach(feature { const coordinates feature.geometry.coordinates; const outerRing coordinates[0].map(c Cesium.Cartesian3.fromDegrees(c[0], c[1]) ); const holes coordinates.slice(1).map(ring ring.map(c Cesium.Cartesian3.fromDegrees(c[0], c[1])) ); const height (feature.properties feature.properties.height) || 10; viewer.entities.add({ name: (feature.properties feature.properties.name) || building, polygon: { hierarchy: new Cesium.PolygonHierarchy(outerRing, holes), extrudedHeight: height, material: Cesium.Color.fromCssColorString(#d6c29a) } }); }); });这段代码的关键点是fromDegrees按经度、纬度的顺序读取坐标因此 GeoJSON 必须是 EPSG:4326 或 EPSG:4490 的经纬度数据如果建筑面有庭院holes数组能把内环正确传入。extrudedHeight是相对地表的拉伸高度如果建筑周边地形起伏大还要结合 DEM 对底部高度做修正否则楼底会悬空或埋入山体。这个方案的瓶颈在几千栋建筑以上时会很明显因为 Entity 数量过多会拖垮绘制帧率。到那个量级就不该再往 Cesium 里塞 GeoJSON而是把轮廓数据切割成 3D Tiles切分时用height做拉伸并在服务器端缓存。这是另一个话题但前提都是前面几步的几何清洗要过关。5. 批量出图和双标注数据上线的两个实用技巧5.1 用 QGIS Atlas 对建筑轮廓批量出图当一个街道的建筑轮廓需要逐栋出图时QGIS 的 Atlas 功能比手动缩放导出快一个数量级。准备一个覆盖图层里面画一个覆盖所有建筑的大矩形再按街道拆成多个矩形或直接使用地块面。在打印布局里添加地图打开“图谱(Atlas)”面板覆盖图层选择地块面勾选“控制可见属性”然后在地图项里设置“随要素缩放”为 1:1000。常规出图参数可以直接照下表参数推荐值说明输出比例1:1000建筑密集区域可换 1:500纸张尺寸A4 或 A3验收资料用 A4重点项目用 A3DPI300至少 300不能小于 150标注字段name floors配合双标注显示地图边距5%防止建筑边界被裁掉导出时下方选“迭代要素”并输出 PDF每栋楼或每个地块就是一页。批量出图最常遇到的问题不是 Atlas 配置而是图层 CRS 与打印布局不一致导致地图框内只有轻微白点。检查流程是布局 → 地图属性 → 重投影到图层 CRS确保所有图层都对齐到同一投影。5.2 同张图显示建筑名称和层数两个标注建筑轮廓出图时往往既要有楼名又要有层数。QGIS 的标注系统支持同一个图层添加多行标注图层属性 → 标注 → 新建一条“名称”标注内容填name再新建一条“层数”标注内容填floors || F。两条标注行都会生效但重叠时文字会打架。解决办法是设置标注优先级。在“标注设置 → 渲染”里把“名称”优先级调高“层数”优先级调低QGIS 会优先绘制名称层数只在有足够空间时显示。比较容易被忽略的是层数标注的数字类型如果floors是字符串使用format_number(floors, 0)转成整型避免出现“4.0F”这种难看结果。5.3 上线前的最终校验数据上线前我通常再做三轮检查第一用“检查有效性”工具把全图层重新跑一遍确保所有面都有效第二叠加最新影像抽 30 个建筑逐栋对比轮廓是否偏移超过 1 米重点看城中村和新建小区第三核对属性表里的年份字段凡是year不是 2022 的要在图例里单独标识不能混进 2022 现状成果。最后用ogr2ogr把基本字段精简为building_id、name、floors、height、year其他临时字段全部丢弃避免多人协作时字段命名冲突。本文还有配套的精品资源点击获取
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进