点云 LAS/LAZ 按 shp 范围裁剪:PDAL pipeline、坐标系对齐与空洞检查

前言

无人机或机载 LiDAR 常交出整测区 LAS/LAZ,甲方只要地块、红线或行政区以内的点。桌面软件框选可以交差,但批次一多就难复现、难审计。把流程写成 PDAL pipeline JSON,把「读点云 → 对齐坐标系 → 按多边形裁剪 → 写出 LAZ」固化下来,适合挂日批或质检脚本。

本文面向已有 LAS/LAZ、会一点命令行的测绘 / GIS 同学,按工程顺序讲三件事:CRS 对齐、filters.crop 裁剪、空洞与边界抽检 。思路上接近正射 DOM 的 gdalwarp -cutline,但对象是离散三维点:没有「半个像元」,坑主要在投影、边界拓扑和裁完后的密度空洞。

关键词:点云、LAS、LAZ、PDAL、shp 裁剪、坐标系、filters.crop、密度检查

环境与输入约定

建议环境:

  • PDAL 2.x(pdal --version 确认)
  • GDAL/OGR(ogr2ogr、ogrinfo,用来处理 shp / GeoJSON)
  • 范围面:Polygon 或 MultiPolygon(.shp 或 GeoJSON)
  • 点云:.las / .laz,最好自带 SRS 元数据

先摸清家底,避免一上来就 crop:

bash 复制代码
pdal info input.laz --metadata | head -n 80
pdal info input.laz --summary
ogrinfo -al -so aoi.shp

重点看:点云 srs / comp_spatialreference、XY 的 bounds、点数 count;shp 的 Layer SRS 与 Extent。若一边是经纬度、一边是投影米制,裁完经常是「点数直接变 0」或「偏了几十上百米还以为成功」。

先对齐坐标系,再谈裁剪

实践里裁剪失败有一半不是多边形坏了,而是 点云与 shp 不在同一 CRS。处理原则:

  1. 优先把矢量重投影到点云 CRS(通常比整库点云重投影便宜)。
  2. EPSG 号一致还不够时,核对 WKT 是否「同名不同参」(地方独立坐标尤甚)。
  3. 点云完全没有 SRS:先补元数据或明确约定,再裁;不要假设「数字看起来像高斯投影就是某带」。

示例(EPSG 请换成 pdal info 读到的真实值):

bash 复制代码
ogr2ogr -t_srs EPSG:4547 aoi_4547.shp aoi.shp
gdalsrsinfo -o wkt aoi_4547.prj | head

若必须把点云变到甲方指定投影,再在 pipeline 里加 filters.reprojection(注意:重投影会改 XY,大文件耗时与误差传递都要评估):

json 复制代码
{
  "type": "filters.reprojection",
  "out_srs": "EPSG:4547"
}

裁剪前用同一单位对比范围:把 pdal info 的 minx/maxx/miny/maxy 与 ogrinfo 的 Extent 画在纸上或 QGIS 里叠一次,确认 AOI 与点云外包确实相交。

最小可跑通的 PDAL pipeline

filters.crop 可接受 bounds 矩形,也可按多边形裁。红线 / 地块交付应用多边形;矩形只适合粗提速预裁。

工程上较稳的做法:先把已对齐 CRS 的 shp 转成 GeoJSON,再交给 crop(避免 shp 组件路径、编码在服务器上的差异)。

bash 复制代码
ogr2ogr -f GeoJSON -t_srs EPSG:4547 aoi_4547.json aoi.shp

clip_las.json 示例:

json 复制代码
{
  "pipeline": [
    {
      "type": "readers.las",
      "filename": "input.laz"
    },
    {
      "type": "filters.crop",
      "filename": "aoi_4547.json"
    },
    {
      "type": "writers.las",
      "filename": "clipped.laz",
      "compression": "laszip"
    }
  ]
}

执行与快速验收:

bash 复制代码
pdal pipeline clip_las.json
pdal info clipped.laz --summary

正常时:count 应明显小于输入;输出 XY bounds 应落在 AOI 外包矩形附近(凹多边形内部镂空除外)。若 count 为 0,立刻回头查 CRS 与 Extent,而不是先怀疑 LAZ 损坏。

多地块、多测区

  • 多要素 shp:默认往往是「落在任一多边形内的点都保留」。若合同是「一地块一文件」,先按 FID / 地块号拆矢量,再循环生成 pipeline。
  • 超大测区 :可先按瓦片 bounds 预裁,再精细 polygon crop,降低单次内存峰值。
  • 只保留某分类 :在 crop 前后按需加 filters.range(例如 Classification[2:2] 只要地面点),但要在说明书写清,避免甲方以为丢了建筑点。

边界、维度与性能上的常见坑

现象 常见原因 处理方向
裁完点数为 0 CRS 错位、度/米混用、AOI 与点云不相交 同单位对比 bounds;重投影矢量后再裁
边界缺一圈点 面自交、未做缓冲、点落在边界外判定 拓扑修复;必要时对 AOI 微缓冲半个点间距
分类 / RGB / GPSTime 丢了 reader/writer 维度未保留、中间 filter 过严 对照 pdal info 维度列表;少加无关 filter
很慢或临时盘暴涨 未压缩写出、一次读入过大 writer 开 laszip;分块 crop 再合并
叠 DOM 对不齐 点云与影像未用同一最终 AOI / CRS 统一 AOI 版本与 EPSG,再分别裁影像与点云

仅做矩形预裁时,可用 crop 的 bounds 形式(形如 ([xmin,xmax],[ymin,ymax]))。最终按合同红线交付时,仍应以多边形结果为准。

空洞与覆盖抽检:裁成功 ≠ 密度合格

pipeline 跑通只说明「范围内还有点」,不说明「测区采全了」。交付前建议固定三步抽检。

1)点数与平均密度

用 AOI 面积(平方米)做粗算:

平均密度 ≈ count / area

对照设计航线或合同指标(例如 ≥ 16 pts/m²)。整体偏低,优先查航高、航向重叠、有效回波,而不是反复改 crop 参数。

2)密度栅格看空洞

把点打成 count / 密度栅格,空洞比看三维更直观。示意 pipeline(分辨率按点间距调整,例如 1 m):

json 复制代码
{
  "pipeline": [
    { "type": "readers.las", "filename": "clipped.laz" },
    {
      "type": "writers.gdal",
      "filename": "density.tif",
      "resolution": 1.0,
      "output_type": "count",
      "data_type": "uint16"
    }
  ]
}

在 QGIS 中叠 AOI:内部成片 0 值或 NoData,属于采集 / 匹配缺口;crop 解决不了,需要补飞或在报告里标缺口区。

3)内缩缓冲抽点

对 AOI 做小幅负缓冲(约 1~2 个点间距)后再 crop 一次。若内缩后点数骤降、外圈几乎无有效点,说明成果贴边稀释,应在元数据或说明书写明有效范围,避免验收时按「满幅均匀密度」卡死。

和 DOM 裁剪怎么分工

对象 常用工具 结果含义
正射 DOM(栅格) gdalwarp -cutline 像元保留或变 NoData/透明
LiDAR 点云 PDAL filters.crop 点保留或丢弃,无半个点

同一项目里,DOM 与点云应使用同一套已对齐的 AOI,并在版本号或文件名里写清日期,避免影像已按新红线裁、点云仍按旧框,叠合时出现「边上一窄条对不上」。

批处理骨架(多 AOI)

测区拆成多个地块时,不必手改 JSON。可以用一段短脚本按目录生成 pipeline 再依次执行(示意):

python 复制代码
from pathlib import Path
import json, subprocess

laz = Path("input.laz")
for aoi in Path("aoi_json").glob("*.json"):
    out = Path("out") / f"{aoi.stem}.laz"
    pipe = {
        "pipeline": [
            {"type": "readers.las", "filename": str(laz)},
            {"type": "filters.crop", "filename": str(aoi)},
            {"type": "writers.las", "filename": str(out), "compression": "laszip"},
        ]
    }
    p = Path("pipes") / f"{aoi.stem}.json"
    p.parent.mkdir(exist_ok=True)
    out.parent.mkdir(exist_ok=True)
    p.write_text(json.dumps(pipe, ensure_ascii=False, indent=2), encoding="utf-8")
    subprocess.check_call(["pdal", "pipeline", str(p)])

跑批后建议把每个输出的 count、bounds 写成 CSV,和地块面积一并入库,后面做密度抽检时直接关联,避免只在口头说「好像都裁过了」。

对已经裁好的结果做抽检清单也可以固化:同名 density GeoTIFF、平均密度字段、是否存在内部空洞标记。这样甲方或质检同事不必再打开三维软件逐块点选。

小结

  1. 先用 pdal info / ogrinfo 对齐 CRS 并确认范围相交,再写 filters.crop。
  2. pipeline JSON 便于复现与批处理;一地块一文件时先拆矢量。
  3. 裁完看 count/bounds,再用密度栅格区分「裁切问题」与「采集空洞」。

你在用 PDAL 按 shp 裁 LAZ 时,最近一次点数异常(变 0 或密度骤降)最终是怎样定位到原因的?

相关推荐
阿琳a_1 天前
b-map-viewer:面向 Vue 3 的地图可视化 SDK,从接入到展示再到交互分析
前端·javascript·vue.js·gis·数据可视化·地图
总要学点什么3 天前
第92篇:三维特效-圆(逐渐消逝)——从圆心开始,让一个洞把整片光啃到只剩外圈
gis·webgl·cesium·空间分析·1024程序员节
vjmap3 天前
AI 地图动画-以真实地图为画布的 AI 视频编辑器-唯杰 AI 地图动画正式上线
人工智能·编辑器·音视频·gis·cad
vjmap4 天前
Cesium + MCP:在 Cursor、Claude Code 里一句话生成地图视频,唯杰 AI 地图动画上线
gis·cesium·mcp
supermapsupport4 天前
SuperMap GIS 基础产品 FAQ 集锦(20261030)
gis·supermap·iserver·idesktopx
god_cvz5 天前
实践:Linux环境构建CTB工具(cesium-terrain-builder)
linux·gis·ctb
梅如你6 天前
ISPRS | 1990-2020年间中国人工林与天然林的空间分布数据
arcgis·gis·遥感
数据库小学妹7 天前
空间数据库 PostGIS 兼容性怎么验?函数、类型、坐标系逐条核(附 SQL)
gis·信创·空间分析·空间数据库·地图渲染
god_cvz7 天前
麒麟 V10(ARM64 / aarch64)源码编译 3dtiles 全步骤
后端·gis