You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
GCSGUI/src/utils/map/geocomputation.ts

166 lines
5.6 KiB
TypeScript

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/*
* @Author: cbwu 504-wuchengbo@htsdfp.com
* @Date: 2024-03-22 15:42:49
* @LastEditors: cbwu
* @LastEditTime: 2024-04-01 14:05:43
* @Description: 地理计算
*/
import {Cartesian3, Cartographic, EllipsoidGeodesic, Math as Cesium_Math, Matrix4, Transforms, Viewer} from 'cesium'
import {Angle} from "@/utils/map/angle.ts";
/**
* 计算空间中一点到一条直线的最短距离的交点(在不考虑地球曲率的情况下)
* @param lineStart 直线的起点:[longitude1, latitude1, height1]
* @param lineEnd 直线的终点:[longitude2, latitude2, height2]
* @param point 空间中的点:[[longitude3, latitude3, height3]]
* @returns 最近的交点
*/
function getClosestPoint(
lineStart: Cartesian3,
lineEnd: Cartesian3,
point: Cartesian3,
): Cartesian3 {
// 计算直线方向向量
const lineDirection = new Cartesian3()
Cartesian3.subtract(lineEnd, lineStart, lineDirection)
Cartesian3.normalize(lineDirection, lineDirection)
// 计算点到直线起点的向量
const pointToStart = new Cartesian3()
Cartesian3.subtract(point, lineStart, pointToStart)
// 计算投影长度,即点到直线的向量在直线方向向量上的分量长度
const projectionLength = Cartesian3.dot(pointToStart, lineDirection)
// 使用标量乘法和向量加法确定交点
const closestPoint = new Cartesian3()
Cartesian3.multiplyByScalar(lineDirection, projectionLength, closestPoint)
Cartesian3.add(lineStart, closestPoint, closestPoint)
return closestPoint
}
/**
* 判断一个点是否在直线上(在不考虑地球曲率的情况下)
* @param lineStart 直线的起点:[longitude1, latitude1, height1]
* @param lineEnd 直线的起点:[longitude2, latitude2, height2]
* @param pointToCheck 直线的起点:[longitude3, latitude3, height3]
* @param tolerance 容差
* @returns 是否在线段上
*/
function isOnLineSegment(
lineStart: Cartesian3,
lineEnd: Cartesian3,
pointToCheck: Cartesian3,
tolerance: number = Cesium_Math.EPSILON1,
): boolean {
const dist_AP = Cartesian3.distance(lineStart, pointToCheck)
const dist_BP = Cartesian3.distance(lineEnd, pointToCheck)
const dist_AB = Cartesian3.distance(lineStart, lineEnd)
const isCollinear = Math.abs(dist_AP + dist_BP - dist_AB) < tolerance
return isCollinear
/*
const startToEnd = new Cartesian3()
const startToPoint = new Cartesian3()
const endToPoint = new Cartesian3()
// 计算向量
Cartesian3.subtract(lineEnd, lineStart, startToEnd)
Cartesian3.subtract(pointToCheck, lineStart, startToPoint)
Cartesian3.subtract(pointToCheck, lineEnd, endToPoint)
// 判断共线
const cross = Cartesian3.cross(startToEnd, startToPoint, new Cartesian3())
console.log('cross:' + Cartesian3.magnitude(cross).toString())
// Math.EPSILON6 是一个非常小的值,用来防止浮点数计算的误差
const isCollinear = Cartesian3.magnitude(cross) < tolerance
// 判断点是否在线段之间
let isBetween = false
if (isCollinear) {
const dotProduct1 = Cartesian3.dot(startToEnd, startToPoint)
const dotProduct2 = Cartesian3.dot(startToEnd, endToPoint)
isBetween = dotProduct1 >= 0 && dotProduct2 <= 0
}
return isBetween
*/
}
/**
* 计算距离,单位米
* @param p1 起点
* @param p2 终点
*/
function getDistance(p1:Cartesian3, p2: Cartesian3): number
{
let point1cartographic = Cartographic.fromCartesian(p1);
let point2cartographic = Cartographic.fromCartesian(p2);
/**根据经纬度计算出距离**/
let geodesic = new EllipsoidGeodesic()
geodesic.setEndPoints(point1cartographic, point2cartographic)
return geodesic.surfaceDistance/1000
}
/**
* 计算方位角,单位度
* @param p1 起点
* @param p2 终点
* @param digits 保留小数位数默认保留1位小数
*/
function getAzimuth(p1:Cartesian3, p2: Cartesian3, digits=1): string
{
// 建立局部坐标系北为y东为xp1为原点
const localMatrix = Transforms.eastNorthUpToFixedFrame(p1)
//求世界坐标到局部坐标的变换矩阵
const worldToLocalMatrix = Matrix4.inverse(localMatrix, new Matrix4())
//p1在局部坐标系的位置即局部坐标原点
const localPosition1 = Matrix4.multiplyByPoint(worldToLocalMatrix, p1, new Cartesian3())
//p2在局部坐标系的位置
const localPosition2 = Matrix4.multiplyByPoint(worldToLocalMatrix, p2, new Cartesian3())
//弧度
const angle = Math.atan2(localPosition2.x - localPosition1.x, localPosition2.y - localPosition1.y)
//转为角度
let theta = angle * (180 / Math.PI);
theta = theta < 0 ? theta + 360 : theta
return theta.toFixed(digits)
}
/**
* 计算多边形面积,单位平方米
* @param vertexs 多边形顶点数组
*/
function getPolygonArea(vertexs: Cartesian3[]) {
let area = 0
for (let i = 0; i < vertexs.length; i++) {
let j = (i + 1) % vertexs.length
area += vertexs[i].x * vertexs[j].y
area -= vertexs[i].y * vertexs[j].x
}
area /= 2
return Math.abs(area)
}
/**
* 获取坐标点的海拔高度
* @param viewer 地图Viewer
* @param pos 坐标点 Cartographic|Cartesian3|[lon,lat]
*/
export function getElevation(viewer: Viewer, pos: Cartographic|Cartesian3|number[]){
let cartographic = undefined
if(pos instanceof Array){
cartographic = Cartographic.fromDegrees(Angle.degree2rad(pos[0]), Angle.degree2rad(pos[1]))
return viewer.scene.globe.getHeight(cartographic)
}
else if(pos instanceof Cartesian3){
cartographic = Cartographic.fromCartesian(pos)
return viewer.scene.globe.getHeight(cartographic)
}
else{
return viewer.scene.globe.getHeight(pos)
}
}
export { getClosestPoint, isOnLineSegment, getDistance, getAzimuth, getPolygonArea }