从声纳到Web3D展示(TODO)

1 多波束声纳原始数据

这个数据通常来自扫测船或者其它测绘设备。因为我这边暂时没有原始数据,所以使用的公开的加州海峡群岛的声纳数据。

数据来源:XYZ format of the 2004 Multibeam Bathymetry Data in the Northeastern Channel Islands Region, Southern California [bathyxyz.zip]

​​在这里,XYZ数据​​ 是描述水下地形或物体空间位置的核心数据集,每个数据点包含 ​​经度(X)、纬度(Y)、水深(Z)​​ 三个维度信息。

-119.69927,34.31851,-89.752
-119.69912,34.31851,-89.753
-119.69897,34.31851,-89.753
-119.69882,34.31851,-89.760
-119.69942,34.31836,-90.522
-119.69927,34.31836,-90.155
-119.69912,34.31836,-89.886
-119.69897,34.31836,-89.817
-119.69882,34.31836,-89.837
-119.69868,34.31836,-89.828
-119.69853,34.31836,-89.752
-119.69838,34.31836,-89.719

2 数据建模

这一步是将xyz文件,转换成3D模型。比如DEM(Digital Elevation Model),这里有几种格式,用的最多的是GeoTIFF。

GeoTIFF​​:

是一种 ​​地理栅格数据容器格式​​(文件格式标准),可以存储 ​​任何类型的栅格数据​​(如高程、温度、卫星影像、分类图等)。 ​​内容自由度​​:可包含单波段(如纯高程)或多波段(如RGB+高程+强度)。 ​​扩展性​​:支持嵌入地理坐标、元数据、压缩算法等。

 这里可以使用OSGeo4W软件Spatial without Compromise · QGIS Web Site,这个是免费的。

安装的大小有点恐怖。。。 

使用OGIS打开的xyz图。

使用IDW进行转换,log如下:

QGIS version: 3.40.9-Bratislava
QGIS code revision: 69fa2ab303
Qt version: 5.15.13
Python version: 3.12.11
GDAL version: 3.11.3
GEOS version: 3.13.1-CAPI-1.19.2
PROJ version: Rel. 9.5.0, September 15th, 2024
PDAL version: 2.9.0 (git-version: 42b180)
Algorithm started at: 2025-07-27T03:13:25
Algorithm 'IDW interpolation' starting…
Input parameters:
{ 'DISTANCE_COEFFICIENT' : 2, 'EXTENT' : '-119.753917583,-118.385515583,33.463768291,34.374771010 [EPSG:4326]', 'INTERPOLATION_DATA' : 'file:///E:/test/bathyll.xyz?type=csv&maxFields=10000&detectTypes=yes&xField=-119.69927&yField=34.31851&zField=-89.752&crs=EPSG:4326&spatialIndex=no&subsetIndex=no&watchFile=no::~::0::~::2::~::0', 'OUTPUT' : 'TEMPORARY_OUTPUT', 'PIXEL_SIZE' : 0.1 }

Execution completed in 30.37 seconds
Results:
  OUTPUT: C:/Users/Administrator/AppData/Local/Temp/processing_vPksAR/1c7f82bd4efc47c7879b5691a0a4e1ea/OUTPUT.tif

Loading resulting layers
Algorithm 'IDW interpolation' finished

最后创建的tif图。(我在这里转换的图很怪,感觉是哪里配置没对。。。)

也可以使用python来进行转换(中间要安装很多库)

import numpy as np
import pandas as pd
from scipy.interpolate import griddata
import rasterio
from rasterio.transform import from_origin

# 读取 xyz 数据
df = pd.read_csv("bathyll.xyz", header=None, names=["lon", "lat", "value"])

# 创建插值网格
grid_x, grid_y = np.mgrid[
    df.lon.min():df.lon.max():500j, 
    df.lat.min():df.lat.max():500j
]
grid_z = griddata((df.lon, df.lat), df.value, (grid_x, grid_y), method='cubic')

# 创建 GeoTransform
res_lon = (df.lon.max() - df.lon.min()) / 500
res_lat = (df.lat.max() - df.lat.min()) / 500
transform = from_origin(df.lon.min(), df.lat.max(), res_lon, res_lat)

# 保存为 GeoTIFF
with rasterio.open("output.tif", "w", 
                   driver="GTiff", 
                   height=grid_z.shape[0],
                   width=grid_z.shape[1],
                   count=1,
                   dtype=grid_z.dtype,
                   crs="EPSG:4326",
                   transform=transform) as dst:
    dst.write(grid_z, 1)

这个就是最后创建的tiff文件。

这个看着正常一些,但是和原始的xyz看着差异也不小。 

3 建模的原理

 GeoTIFF 是规则栅格数据(即每个像素有一个固定大小),属于数字高程模型(DEM)。而XYZ 是散点。所以要进行转换。

规则栅格数据​​(Regular Grid Data)是一种 ​​结构化​​ 的空间数据表示形式,由 ​​均匀分布的像素(或网格单元)​​ 组成,每个像素包含一个数值(如高程、温度、浓度等)。其核心特征如下: ​​

几何规则性​​

所有像素大小相同(如 1m×1m),按行列整齐排列。 地理范围由 ​​原点坐标​​(左上角/左下角)和 ​​像素大小​​ 唯一确定。 ​

数据存储​​

以矩阵形式​​存储,每个像素值可通过行列号(row, col)快速定位。

文件格式:GeoTIFF、ASCII Grid、NetCDF、HDF5 等。

特征点云(Point Cloud)栅格(Raster/Grid)
数据结构非规则、稀疏规则、密集的网格
数据量仅保存有值的点保存整个区域的所有格子(无论是否有值)
空间表达离散连续(插值)
存储精简,适合稀疏场景冗余,适合密集连续场景
应用方向三维重建、激光雷达地图、影像、DEM、遥感

适用场景:

场景更适合点云更适合栅格
三维扫描/激光雷达
遥感影像、地形图
稀疏海底测深数据
地形分析(坡度、流向)
AI 图像识别训练

点云就是点集合的描述,栅格就是将点数据放到矩阵。两者最大的不同就是存储的数据格式不同,数据一个是不连续一个是连续。使用栅格的最大好处就是方便运算,可以直接使用很多矩阵运算,比如卷积、滤波、插值等,也可以直接用GPU来加速。

将原始点云转化成栅格需要:离散的点云 → 连续的场 → 栅格化

算法有:

IDW(反距离加权)

插值 Kriging(克里金插值)

TIN三角网再栅格化

4 LAS和LAZ

las和laz也是点云文件,但是是二进制的。laz包含压缩。

LAS的文件头(Header):固定 375 字节

字段字节长度说明
文件签名(File Signature)4 字节固定为 “LASF”,用于识别 LAS 文件。
文件版本(Version)2 字节如 “1.4”(主版本号 1,次版本号 4),不同版本支持的属性字段有差异。
系统标识(System ID)32 字节生成数据的系统名称(如传感器型号),不足 32 字节用空字符填充。
点云总数(Point Count)8 字节记录文件中点的总数量(64 位整数,支持超大规模点云)。
坐标范围(Min/Max X/Y/Z)48 字节6 个双精度浮点数(各 8 字节),记录点云在 X、Y、Z 轴上的最小 / 最大值,用于快速定位空间范围。
坐标比例因子(Scale X/Y/Z)24 字节3 个双精度浮点数,用于将点数据中的整数坐标转换为实际物理坐标(见下文 “坐标存储原理”)。
坐标偏移量(Offset X/Y/Z)24 字节3 个双精度浮点数,与比例因子配合实现坐标转换。
点数据格式(Point Data Format)1 字节定义点数据记录的结构类型(如 0~6,不同类型支持的属性字段不同,例如格式 3 支持 RGB 颜色)。
点数据长度(Point Record Length)2 字节每个点数据记录占用的字节数(如格式 3 对应 34 字节 / 点)。

点数据记录(Point Data Records):核心数据区

字段字节长度说明
X/Y/Z 坐标(整数)4 字节 ×3实际坐标需通过公式计算:实际坐标 = 整数坐标 × 比例因子 + 偏移量(见下文)。
反射强度(Intensity)2 字节0~65535 的整数,反映激光回波强度(材质越亮,强度越高)。
回波信息(Return Flags)1 字节二进制位标识(如第 1 位表示是否为首次回波,第 3 位表示是否为末次回波)。
分类标签(Classification)1 字节整数代码(如 2 = 地面、6 = 建筑物、9 = 植被),用于点云分类。
RGB 颜色2 字节 ×3红、绿、蓝三通道值(0~65535),记录点的颜色信息。

有两种方法转换,一种是代码一种是pdal工具。

PDAL转换

运行命令: 

pdal pipeline xyz2las.json

xyz2las.json

{
  "pipeline": [
    {
      "type": "readers.text",
      "filename": "bathyll.xyz",
      "header": "X,Y,Z",
      "separator": ","
    },
    {
      "type": "writers.las",
      "filename": "output.las"
    }
  ]
}

pdal info output.las

tom@PC-20241221RKUQ:~/test$ pdal info output.las
{
  "file_size": 131792591,
  "filename": "output.las",
  "now": "2025-07-27T22:25:29+0800",
  "pdal_version": "2.6.2 (git-version: Release)",
  "reader": "readers.las",
  "stats":
  {
    "bbox":
    {
      "native":
      {
        "bbox":
        {
          "maxx": -119.05,
          "maxy": 34.32,
          "maxz": -25.66,
          "minx": -119.7,
          "miny": 33.89,
          "minz": -1244.45
        },
        "boundary": { "type": "Polygon", "coordinates": [ [ [ -119.7, 33.89, -1244.45 ], [ -119.7, 34.32, -1244.45 ], [ -119.05, 34.32, -25.66 ], [ -119.05, 33.89, -25.66 ], [ -119.7, 33.89, -1244.45 ] ] ] }
      }
    },
    "statistic":
    [
      {
        "average": -119.3893761,
        "count": 3876246,
        "maximum": -119.05,
        "minimum": -119.7,
        "name": "X",
        "position": 0,
        "stddev": 0.1361889993,
        "variance": 0.01854744352
      },
      {
        "average": 34.07363823,
        "count": 3876246,
        "maximum": 34.32,
        "minimum": 33.89,
        "name": "Y",
        "position": 1,
        "stddev": 0.1018629814,
        "variance": 0.01037606697
      },
      {
        "average": -328.1332479,
        "count": 3876246,
        "maximum": -25.66,
        "minimum": -1244.45,
        "name": "Z",
        "position": 2,
        "stddev": 233.8208149,
        "variance": 54672.17348
      },
......

python转换

import numpy as np
import laspy

# 1. 读取 xyz 文件
xyz_file = "input.xyz"
xyz = np.loadtxt(xyz_file, usecols=(0, 1, 2))  # 只读取前三列,作为 x,y,z

# 2. 创建 LAS header
header = laspy.LasHeader(point_format=3, version="1.2")  # 可选点格式和版本
header.offsets = np.min(xyz, axis=0)
header.scales = np.array([0.001, 0.001, 0.001])  # 设置精度

# 3. 创建 LAS 文件并写入点
las = laspy.LasData(header)
las.x, las.y, las.z = xyz[:, 0], xyz[:, 1], xyz[:, 2]

# 4. 保存为 .las 文件
las.write("output.las")
print("转换完成:output.las")

LAZ则是压缩过的LAS文件。

5 在Web的显示

在web显示中,需要使用3D tile。整体转换如下:

数据格式描述转换路径(→ 3D Tiles)
.tif (DEM)高程栅格tif → terrain tiles → Cesium 可加载
.tif (影像)遥感地图tif → WMS/WMTS 叠加 → Cesium
.xyz点云(文本).xyz.las → 3D Tiles
.las/.laz标准点云格式直接 → 3D Tiles
.obj/.gltf3D模型直接 → 3D Tiles

生成LAS之后,可以直接在Cesium网站转换。

Cesium ion

设置了位置之后,就会自动在地图上显示。(我的地图感觉设置还是有点问题。。。)

另外一种方式是使用命令转换。 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值