GIS 相关

GIS 相关

简介

最简单的测绘基础知识介绍。

正射影像图

• 定义:正射影像图,orthophoto map,简称 DOM。它是通过对航拍/卫星原始影像进行 几何校正(消除地形起伏、相机倾斜、畸变),让影像中的地物位置与地图上的实际地理坐标对应起来。
• 特点:
• 无畸变(像素位置和地面坐标一一对应)
• 可直接量测(可以量距离、面积等,结果和地图一致)
• 保留了影像的纹理信息(不像矢量图那样抽象)
来源 空间分辨率(像素大小) 定位精度(几何精度) 应用场景
卫星影像(商业高分) 0.3–2 m 1–5 m 区域级规划
航空摄影 10–50 cm 20–100 cm 城市级测绘
无人机航测 2–10 cm 5–20 cm 工程、矿山、大坝

精度一般要求:

  • 平面精度 ≈ 影像分辨率的 1–2 倍
  • 高程精度 ≈ 分辨率的 2–3 倍

例如:分辨率 5 cm 的无人机 DOM,平面精度可以做到 5–10 cm。

坐标系

在测绘、GNSS 定位等领域,有很多的坐标系。

坐标系 原点 轴向 用途
ECEF 地球质心 X→本初子午线赤道交点,Z→北极 卫星导航、全球定位
大地坐标系 地球表面 纬度、经度、高程 GNSS原始数据
ENU 地表某点 E→东,N→北,U→天顶 局部工程监测(如大坝、滑坡)
  • EPSG 原指 European Petroleum Survey Group。是目前 GIS 软件中最权威的坐标系数据库
  • ENU (East-North-Up)坐标系是一种局部直角坐标系,需通过原点(lat₀, lon₀, h₀)(如监测点初始位置)将大地坐标转换为ENU。特点是:1、直观:东、北、天顶方向与日常地理方向一致,便于工程人员理解。2、局部精度高:在几十公里范围内,ENU坐标系可近似为平面直角坐标,忽略地球曲率误差。3、方便旋转:后续转换到大坝坐标系时,只需绕U轴旋转一个角度(γ)即可。常用于局部工程监测。

取决于应用范围和数据提供方:

  • 国家级测绘 / 工程测量:CGCS2000(EPSG:4490 / 4544 / 4554 等投影坐标)
  • 全球数据 / 卫星影像:WGS84(EPSG:4326,或者投影成 UTM 坐标系)
  • 工程局部坐标: 方便施工/监测的小范围坐标系,一般是投影坐标经过 平移+旋转 得到。需定义坐标系的原点和方向
坐标系 介绍
EPSG:4326 (WGS84 经纬度坐标) 地理坐标系,和 CGCS2000 很像,只是基准不同(误差小于 1m,普通应用中基本可以互换)。很多国际数据源都用这个。
EPSG:4490 (CGCS2000 经纬度坐标) CGCS2000 原始定义,大地基准,单位:度(经纬度)。中国官方规定的大地坐标系统。和 WGS84 椭球误差极小。注意 4490 是地理 2D 坐标(纬度、经度,单位度);高程不属于 4490 本身的定义。
EPSG:4544 (CGCS2000 的投影坐标系) 基础上采用 3 度带高斯-克吕格投影,中央经线 105°E。投影坐标系(Projected CRS),坐标单位是 米,常用于测绘、工程建设。
EPSG:3857(WGS84 投影坐标系 采用 圆柱等角(正形)投影,(单位米)。把地球近似为 正球体,而非 WGS84 的椭球体。一般用于电子地图。

注意:

  • 地理坐标系的原点是 纬度 0° = 赤道, 经度 0° = 格林尼治本初子午线 ,高程 0 m = 只是椭球面的数学定义,不一定对应真实海平面或陆地表面。
  • 投影坐标系一般是 2D(X、Y) + 高程。原点一般是自己人为定义的(毕竟球面只能在小范围内视为平面).投影平面 0 点的定义(East=0, North=0)。每个投影都有自己的“平面原点”,与真实经纬度或空间直角原点毫无关系。

常见几种投影坐标系的原点定义:

1、高斯-克吕格 / Gauss-Krüger

• 原点取中央经线 L₀ 与 赤道 的交点;
• 为防负坐标,通常把 X 轴西移 500 000 m(中国统一加 500 km,称“东伪偏移”)。

因此中国国内坐标:(X, Y) = (0, 0) 实际在赤道、中央经线以西 500 km 处。

2、UTM

• 原点也是中央经线 L₀ 与赤道交点;
• 规定东伪偏移 500 000 m,北半球北伪偏移 0 m,南半球北伪偏移 10 000 000 m。

所以 UTM Zone 50N 的 (0, 0) 在赤道、117°E 以西 500 km。

3、任意工程独立坐标系

• 设计单位可自定义原点:常选坝轴线左端点、或城市中心某点;
• 通过平移参数 East₀, North₀ 直接给出“该点对应 (0,0)”。

适用于电子地图显示的 EPSG:3857 就是“Web 墨卡托投影”,米为单位。成为 Web 地图事实标准(Google Maps、OpenStreetMap、Mapbox、Leaflet 等默认底图坐标系)

  • Web Mercator 把地球先当成一个 球(而不是更精确的椭球),再把球面投影到一个 圆柱面,最后展开成平面。
  • 该投影是 等角(保形) 的,(形状、方向保持不变),因此 纬线方向的尺度随纬度急剧放大(误差来自 投影公式本身)。
  • 方便在二维画布或 WebGL 里直接做平移、缩放、距离量算。计算简单,浏览器/移动端渲染效率高。对导航、定位、路径规划最直观。
  • Web Mercator 投影把地球切成 256×256 像素的正方形瓦片,层级递增,浏览器加载逻辑固定,缓存复用率高

数据存储通常用 4326,显示/切片时转为 3857。

对于单次项目,我们会涉及到多种坐标系:

  • 大坝坐标系:仅用于本次 GNSS 位移监测的“大坝坐标系”,原点就是本次选定的基准点(GNSS 站心);通过一次旋转把 ENU → (ΔX坝, ΔY坝, ΔZ坝) 即可使用,因此它是一种非常局部的、临时的小坐标系。→ 仅解决“本次监测位移”这一件事;
  • EPSG:4544(CGCS2000 / 3° Gauss-Krüger 120°E)是整个工程项目采用的官方投影坐标系;原点在国家规范里(120°E 与赤道交点西移 500 km);用于全坝段、全生命周期的图纸、施工、验收、档案;解决“整个大坝工程所有几何信息”的统一基准。
  • EPSG:3857,当正射影像图需切片在地图上显示的时候,需要用 EPSG:3857 这个坐标系。因为电子地图普遍都是这个坐标系。

地图如何显示

如果你的正射影像图最终要发布成 在线地图切片,通常流程就是:

  • 原始影像 → EPSG:4326/WGS-84(或 EPSG:4544 等工程坐标)
  • 用 gdalwarp 或 QGIS 一次性重投影到 EPSG:3857
  • 再用 gdal2tiles、MapProxy、GeoServer 等切成 3857 瓦片并发布

我们拿到三个文件:tif 文件、prj文件、tfw 文件。其中 tif 文件介绍如下:

常见的 正射影像图数据格式:GeoTIFF (.tif):最常见的格式,TIFF 影像 + 内嵌空间参考信息。外加一个 .tfw / .prj 文件(如果是裸 TIFF 没内嵌坐标时)。 正射影像图(Orthophoto)的 .tif 文件是一种地理空间栅格格式(GeoTIFF)。它把 普通图像内容 和 空间定位信息 同时封装在一个文件里,因此既能像 PNG/JPG 那样看图,又能像地图那样精确量测坐标。

类别 内容 存储方式 备注
栅格图像数据 每个像素的灰度/颜色值 按行或按瓦片(Tile)存储 相当于 PNG 的“图片”
空间定位信息 仿射变换参数(6 个系数)或 RPC 系数 GeoTIFF 标签(Tag 33922、33550、34737 等) 告诉软件:像素 (i,j) ↔ 地面 (E,N) 的数学关系
坐标系/投影信息 EPSG 代码、WKT 投影字符串 GeoTIFF 标签 GeoKeyDirectory(Tag 34735) 例如 EPSG:4490、EPSG:32650

prj文件:就是对 EPSG-4544 的一段标准定义

PROJCS["CGCS2000 / 3-degree Gauss-Kruger CM 105E",GEOGCS["China Geodetic Coordinate System 2000",DATUM["China_2000",SPHEROID["CGCS2000",6378137,298.257222101,AUTHORITY["EPSG","1024"]],AUTHORITY["EPSG","1043"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4490"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",105],PARAMETER["scale_factor",1],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AUTHORITY["EPSG","4544"]]


名称 "CGCS2000 / 3-degree Gauss-Kruger CM 105E"
表示这是以 CGCS2000 大地坐标系(中国大地2000坐标系)为基础,采用 3度带高斯-克吕格投影(中央经线105°E) 的投影坐标系( 高斯-克吕格投影是一种将地球椭球面上的地理坐标(经纬度)正形投影到二维平面上的方法)。

GEOGCS[“China Geodetic Coordinate System 2000”, …]
表明其大地基准是 CGCS2000,是中国国家级大地坐标系,跟 WGS84 接近。


DATUM[“China_2000”, SPHEROID[“CGCS2000”,6378137,298.257222101]]
基准面采用椭球体参数:长半轴 = 6378137 m,扁率倒数 = 298.257222101。这是标准的 CGCS2000 椭球。


PROJECTION[“Transverse_Mercator”]
横轴墨卡托投影(高斯-克吕格就是横轴墨卡托的一个实现)。

latitude_of_origin = 0 → 投影起始纬度为赤道
central_meridian = 105 → 中央经线是东经 105°
scale_factor = 1 → 投影比例因子 1.0
false_easting = 500000 → 假东距 500km(防止出现负坐标)
false_northing = 0 → 假北距 0

UNIT[“metre”,1]
投影结果的单位是米。

AUTHORITY[“EPSG”,“4544”]
EPSG 编号 4544,对应 CGCS2000 / 3-degree Gauss-Kruger zone 35(中央经线105°E)


以上信息说明:EPSG:4544 投影坐标系的“原 点”对应 (105°E, 0°N),中央子午线与赤道的交点,X轴:中央子午线的投影。Y轴:赤道的投影。
但因为约定俗称,要有 false easting = 500,000 m,所以这个点在平面上是 (500000, 0)
在这个投影坐标系上,正射影像上每个像素的坐标确实是以 米为单位的 (E,N)。所以,如果输出的位置给了 (E、N)这样的坐标,需要转换成 EPSG:4490 才能用于地图显示。使用 gdal 工具:

# 使用 gdaltransform(把 E N 放在 stdin)
echo "605732.799 3999843.061 1623.661" | gdaltransform -s_srs EPSG:4544 -t_srs EPSG:4490
# 通常输出:<lon> <lat> <h>

总结,这段文件是:

EPSG:4544 的完整投影坐标系描述(WKT 与关键参数) EPSG:4544 的官方名称如下:

CGCS2000 / 3-degree Gauss-Kruger CM 105E , 中文习惯叫“国家 2000,三度分带,中央经线 105°E,坐标不含带号”。
投影方法:横轴墨卡托(Transverse Mercator,EPSG 代码 9807)。
核心参数:
中央经线(Longitude of natural origin):105°E
原点纬度(Latitude of natural origin):0°
比例因子(Scale factor):1.0
假东移(False easting):500 000 m
假北移(False northing):0 m
线性单位:米(EPSG:9001)

椭球体与基准
基于 China Geodetic Coordinate System 2000(EPSG:4490):
椭球:CGCS2000
长半轴 a = 6 378 137 m
扁率 1/f = 298.257 222 101

适用区域
中国陆上 103°30′E – 106°30′E 之间的狭长条带

tfw 文件: TFW 文件(通常和 .tif 配套使用,叫 World File)就是一个 栅格影像的空间参考文件。它的作用是告诉 GIS 软件:这张图像(.tif)应该放到地图上的哪个位置,缩放和旋转关系如何。

0.037220000000
0
0
-0.037220000000
602832.567790000001
4001299.158060000278

Line 1: 像素大小(X 方向,每像素宽度,单位:地图坐标单位)
Line 2: 行旋转参数(一般为 0)
Line 3: 列旋转参数(一般为 0)
Line 4: 像素大小(Y 方向,注意通常是负值,因为影像行号向下而坐标系 Y 轴向上)
Line 5: 左上角像素中心的 X 坐标
Line 6: 左上角像素中心的 Y 坐标

此内容说明:图片的像素大小是 每个像素长宽是 0.03722 米,3.7 厘米/像素。
左上角位置在 投影坐标系下的 (602832.57, 4001299.16),单位米。

高程

高程也有多种

名称 定义 基准面 与水准测量的关系 典型数值差异
椭球高(Ellipsoidal Height, h 某点沿椭球法线到参考椭球面的距离 数学椭球面(CGCS2000、WGS-84 等) 与水准测量无直接关系 在中国区域通常比正高高 30 m 左右(可正可负)
正高(Orthometric Height, H 某点沿铅垂线到大地水准面(平均海水面)的距离 大地水准面(似大地水准面) 就是日常“海拔高程”,用水准仪测得 与椭球高的关系:h = H + NN 为大地水准面差距)

在一般的土木、水利、交通等工程里,“高程”默认指的就是正高(或我国采用的正常高),也就是“海拔高”;只有在做 GNSS/GPS 测量、坐标转换或与卫星数据打交道时,才会先拿到椭球高,随后必须再换算成正高才能用于设计、施工、监测。

为什么工程图纸上不写“椭球高”

  • 设计、施工、监测都要与水准点、水位、防洪高程、结构限界等对比,而这些数据全部由水准测量给出,属于 1985 国家高程基准(正常高系统)。
  • 椭球高与正常高在我国差值可达 20–40 m,直接用会导致坝顶高程、桥墩标高、隧道埋深等统统错位。

因此,《工程测量规范》《水利水电工程测量规范》等所有行业规范均明确规定:最终成果必须归算到 1985 国家高程基准(正常高)。

如果用中国区域大地水准面模型(如 CNGG2015、CQG2000 或各省精化模型)把每个监测点的椭球高 h 改正为正高 H:
H = h − N
其中 N 为大地水准面差距,是基于电子格网文件做差值计算得到。

切片格式

最常用的三种栅格地图切片标准——TMS、WMTS、XYZ 对比:

方面 TMS (Tile Map Service) WMTS (Web Map Tile Service) XYZ (a.k.a. “Slippy Map” / “Google XYZ”)
标准组织 OSGeo(开源) OGC(开放地理空间联盟) 事实标准(Google、OSM、Mapbox 等)
协议形式 REST(纯 HTTP GET) 支持三种:KVP、SOAP、REST REST(最简 GET)
元数据接口 /tilemapresource.xml 描述层级、范围、原点、像素尺寸 标准 GetCapabilities(XML) 无官方元数据接口(靠文档或代码写死)
原点 & 轴向 左下角原点;Y 从下往上递增(0,1,2…) 左上角原点;Y 从上往下递增(0,1,2…) 与 WMTS 相同:左上角原点,Y 向下
URL 模板示例 http://host/tms/1.0.0/layer/z/x/y.png REST 形式:
…/wmts?layer=xxx&TileMatrix=z&TileRow=y&TileCol=x
https://host/{z}/{x}/{y}.png
坐标系/投影 任意,但默认 Web Mercator(EPSG:3857) 任意,通过 TileMatrixSet 显式声明 绝大多数是 Web Mercator;少量 WGS84
图块尺寸 256×256 或 512×512(XML 里声明) 256×256 常见,也可自定义 256×256 最常见
文件/图片格式 PNG、JPEG PNG、JPEG、GIF、TIFF 等 PNG、JPEG
缓存友好度 极高(静态目录即可托管) 高(支持 HTTP 缓存头) 极高(CDN 最爱)
客户端示例 Leaflet、OpenLayers、MapProxy Leaflet、OpenLayers、ArcGIS API Leaflet、Mapbox GL、Cesium、OpenLayers
易踩的坑 y 轴方向与 XYZ 相反,需要 y = (2^z - 1) - y_tms 转换 Capabilities XML 较冗长,解析略麻烦 无官方规范,不同厂商可能在原点、Z 起点上“魔改”

一句话总结

  • TMS:目录结构简单,Y 轴从下往上,适合离线包或静态服务器。
  • WMTS:OGC 标准,功能最全(多投影、多格式、GetFeatureInfo),但调用稍重。
  • XYZ:最轻量、最流行,Y 轴向下,URL 模板一目了然,几乎所有现代地图 SDK 都默认支持。

脚本工具

主要使用 gdal 工具,比较快。用 GIS 桌面工具安装麻烦,而且速度很慢。

1、查看 tif 文件信息:说明 tif 内置了空间定位信息(也就是 prj 和 tfw 的信息)

$ gdalinfo  dzh.tif

Driver: GTiff/GeoTIFF
Files: dzh.tif
Size is 86842, 67258
Coordinate System is:
PROJCRS["CGCS2000 / 3-degree Gauss-Kruger CM 105E",
    BASEGEOGCRS["China Geodetic Coordinate System 2000",
        DATUM["China 2000",
            ELLIPSOID["CGCS2000",6378137,298.257222101,
                LENGTHUNIT["metre",1]]],
        PRIMEM["Greenwich",0,
            ANGLEUNIT["degree",0.0174532925199433]],
        ID["EPSG",4490]],
    CONVERSION["Gauss-Kruger CM 105E",
        METHOD["Transverse Mercator",
            ID["EPSG",9807]],
        PARAMETER["Latitude of natural origin",0,
            ANGLEUNIT["degree",0.0174532925199433],
            ID["EPSG",8801]],
        PARAMETER["Longitude of natural origin",105,
            ANGLEUNIT["degree",0.0174532925199433],
            ID["EPSG",8802]],
        PARAMETER["Scale factor at natural origin",1,
            SCALEUNIT["unity",1],
            ID["EPSG",8805]],
        PARAMETER["False easting",500000,
            LENGTHUNIT["metre",1],
            ID["EPSG",8806]],
        PARAMETER["False northing",0,
            LENGTHUNIT["metre",1],
            ID["EPSG",8807]]],
    CS[Cartesian,2],
        AXIS["northing (X)",north,
            ORDER[1],
            LENGTHUNIT["metre",1]],
        AXIS["easting (Y)",east,
            ORDER[2],
            LENGTHUNIT["metre",1]],
    USAGE[
        SCOPE["Cadastre, engineering survey, topographic mapping (large scale)."],
        AREA["China - between 103°30'E and 106°30'E."],
        BBOX[22.5,103.5,42.21,106.51]],
    ID["EPSG",4544]]
Data axis to CRS axis mapping: 2,1
Origin = (602832.567790000000969,4001299.158060000278056)
Pixel Size = (0.037220000000000,-0.037220000000000)
Metadata:
  TIFFTAG_SOFTWARE=pix4dmapper
  AREA_OR_POINT=Area
Image Structure Metadata:
  COMPRESSION=LZW
  INTERLEAVE=PIXEL
  PREDICTOR=2
Corner Coordinates:
Upper Left  (  602832.568, 4001299.158) (106d 8'32.85"E, 36d 8'11.59"N)
Lower Left  (  602832.568, 3998795.815) (106d 8'31.68"E, 36d 6'50.39"N)
Upper Right (  606064.827, 4001299.158) (106d10'42.11"E, 36d 8'10.34"N)
Lower Right (  606064.827, 3998795.815) (106d10'40.89"E, 36d 6'49.13"N)
Center      (  604448.697, 4000047.487) (106d 9'36.88"E, 36d 7'30.37"N)
Band 1 Block=86842x1 Type=Byte, ColorInterp=Red
  Mask Flags: PER_DATASET ALPHA
Band 2 Block=86842x1 Type=Byte, ColorInterp=Green
  Mask Flags: PER_DATASET ALPHA
Band 3 Block=86842x1 Type=Byte, ColorInterp=Blue
  Mask Flags: PER_DATASET ALPHA
Band 4 Block=86842x1 Type=Byte, ColorInterp=Alpha

2、 重投影为 EPSG:3857

GeoTIFF 文件已经带了 坐标系 (EPSG:4544) 信息。因为要生成网页上显示的切片,需要投影为 EPSG:3857 坐标系:

gdalwarp -t_srs EPSG:3857 \
  -tr 1 1 \
  -r bilinear \
  -of GTiff \
  -co COMPRESS=DEFLATE \
  -co TILED=YES \
  -dstalpha \
  input.tif output_3857.tif

说明:

-t_srs EPSG:3857 :投影到 Web 墨卡托。
-tr 1 1 :设置输出分辨率为 1 米 × 1 米(在 EPSG:3857 的单位是米)。建议先用 gdalinfo input.tif | grep "Pixel Size" 看一下原始像元大小,再决定 -tr。
-r bilinear :双线性插值,适合连续值(影像类数据),如果是分类数据(如土地利用),建议用 -r near。
-of GTiff :输出 GeoTIFF。
-co COMPRESS=DEFLATE :压缩。
-co TILED=YES :块存储,利于快速切片。
-dstalpha :生成透明通道(让无数据区域透明)。

3、切片

gdal2tiles.py -z 0-18 -r bilinear -p mercator -a 0 \
  --processes=4 warped.tif tiles/ --tilesize=256 --tiledriver=PNG

GNSS位移量计算

有的 gnss 设备上报的数据是 大小+水平角+垂直角,需要转换成相对位移,并且还要映射到大坝坐标系。

大坝坐标系定义说明: Y轴 : 平行于坝轴线,面朝上游,右侧为正,左侧为负。 X轴 : 垂直于坝轴线,下游为正,上游为负。 Z轴 : 垂直于地面,向上为正,向下为负。

在坝轴上取两个点,计算 Y 轴与正北方的 位角 θ。可以得到一个关键信息,来定义大坝坐标系。

原理解析

根据我们的大坝坐标系定义:

• Y 轴:沿坝轴线,面向上游,右侧为正(即轴指向下游)。 • X 轴:垂直坝轴线,下游为正。 • Z 轴:向上为正。

ENU → 大坝坐标系的旋转矩阵 R 如下:

      ⎡  cosθ   sinθ   0 ⎤
R  =  ⎢ –sinθ   cosθ   0 ⎥
      ⎣   0       0    1 ⎦

计算公式:

|ΔX坝|   | cosθ   sinθ   0 |   |ΔE|
|ΔY坝| = |-sinθ   cosθ   0 | · |ΔN|
|ΔZ坝|   |  0      0     1 |   |ΔU|

即

ΔX坝 =  ΔE·cosθ + ΔN·sinθ
ΔY坝 = –ΔE·sinθ + ΔN·cosθ
ΔZ坝 =  ΔU

转换代码

计算水平方位角的 python 代码:

θ = atan2(ΔE, ΔN) (0° 指北,90° 指东,顺时针增加),θ 就是坝轴在水平面内与正北的夹角。

import math
from pyproj import Proj, Transformer

# 输入:两端点经纬度(度)
# lat1, lon1 = 36.134616757252154,106.13848686218262
# lat2, lon2 = 36.13080407775483,106.18852615356447

lat1, lon1 = 36.13822130205884,106.17324829101564
lat2, lon2 = 36.11617553449072,106.17470741271974

# 定义 CGCS2000 / 3-degree Gauss-Kruger 投影
# 这里假设中央子午线为 105E
proj = Proj(proj='tmerc', lat_0=0, lon_0=105, k=1, x_0=500000, y_0=0, ellps='GRS80')

# 经纬度转平面坐标
x1, y1 = proj(lon1, lat1)
x2, y2 = proj(lon2, lat2)

# 坝轴向量
dx = x2 - x1
dy = y2 - y1

# 计算方位角 θ(顺时针从正北算起)
theta_rad = math.atan2(dx, dy)  # 注意 dx 对应东向,dy 对应北向
theta_deg = math.degrees(theta_rad)
if theta_deg < 0:
    theta_deg += 360

print(f"坝轴方位角 θ = {theta_deg:.3f}°")%

// GNSS设备上报的数据。
// DzhGnssRealtimeData 保存dzhGnss设备的最新实时值
type DzhGnssRealtimeData struct {
	Lat       float64 `json:"lat"`       // 纬度
	Lon       float64 `json:"lon"`       // 经度
	Alt       float64 `json:"alt"`       // 海拔高度,单位 m
	X         float64 `json:"x"`         // X方向位移,单位 mm
	Y         float64 `json:"y"`         // Y方向位移,单位 mm
	Z         float64 `json:"z"`         // Z方向位移,单位 mm
	Power     float64 `json:"power"`     // 电压
	RSSI      int     `json:"rssi"`      // 信号强度
	Timestamp int64   `json:"timestamp"` // Unix时间戳
}

/*
"data":"{
	"lat":36.123160717,
	"lon":106.17444795,
	"alt":1620.93103,
	"drft":0.0047,
	"drfH":270,
	"drfV":0,
	"mod":4,
	"pow":12.3,
	"RSSI":22,
	"clientId":"sv78dRYjrD1eICq51QcD",
	"detectedTime":"2025-08-11 13:31:32"
}
drft 是位移大小,单位是米(m),两个点之间的间距
drfH 是水平角,单位是度(°),这里假设角度是以正北为0度,顺时针方向
drfV 是垂直角,单位是度(°),正值表示向上仰角,负值表示向下

drft = 位移大小(径向距离 r)
drfH = 水平方位角(θ,北 0° 顺时针)
drfV = 垂直仰角(φ,水平 0°,上正下负)

位移量,单位米
水平角度,单位度,此角度为设备所在水平面的位移方向,正北为0度,按顺时针增大,正东为90度
垂直角度,单位度,此角度为设备所在垂直方向的位移方向,水平为0度,向下最大垂直方向-90度,向上最大垂直方向90度
*/


// 位移(大小+水平角+垂直角) 转换为 ENU 三维向量
// drft 单位 m,drfH 水平角(°,北0°顺时针),drfV 垂直角(°,正上仰角)
// (北+、东+、上+),相反是负的

// 球面位移转 ENU
func PolarToVector(drft, drfH, drfV float64) (dN, dE, dU float64) {
	// 角度转弧度
	hRad := drfH * math.Pi / 180.0
	vRad := drfV * math.Pi / 180.0

	// 分解
	hLen := drft * math.Cos(vRad) // 水平投影
	dE = hLen * math.Sin(hRad)    // 东向   	E (East):向东(经度增大的方向)
	dN = hLen * math.Cos(hRad)    // 北向       N (North):向北(纬度增大的方向)
	dU = drft * math.Sin(vRad)    // 垂直分量    U (Up):向上(椭球面法线方向,即大地高程方向)

	// x, y z := dE,dN, dU

	return
}


// ENU 转大坝坐标系
// Y轴:沿坝轴线方向,面朝上游,右侧为正,左侧为负
// X轴:垂直于坝轴线,下游为正,上游为负
// Z轴:竖直向上

func VectorToDam(dN, dE, dU, theta float64) (dX, dY, dZ float64) {
	// θ = 坝轴线相对正北的方位角,顺时针为正
	thRad := theta * math.Pi / 180.0

	// 沿坝轴方向 → Y
	dY = dN*math.Cos(thRad) + dE*math.Sin(thRad)

	// 垂直坝轴方向 → X(下游为正)
	dX = -dN*math.Sin(thRad) + dE*math.Cos(thRad)

	// 垂直方向保持不变
	dZ = dU

	return
}
Last updated on September 3, 2025