Files
sdk4.0/src/Tools/index.js

1319 lines
42 KiB
JavaScript
Raw Normal View History

2025-07-03 13:54:01 +08:00
/**
* @name: index
* @author: Administrator
* @date: 2022-06-09 16:46
* @descriptionindex
* @update: 2022-06-09 16:46
*/
import md5 from 'js-md5'
import { Proj } from './proj'
import { open as projConvertOpen, close as projConvertClose } from './projConvert'
import { open as projectionConvertOpen, close as projectionConvertClose } from './projectionConvert'
import { setActiveViewer, closeRotateAround, closeViewFollow } from '../Global/global'
2025-07-07 16:21:01 +08:00
import FlowPictureMaterialProperty from '../Obj/Materail/FlowPictureMaterialProperty'
2025-07-03 13:54:01 +08:00
class Tools {
/**
* @constructor
* @desc 工具类
* */
constructor(sdk, options) {
this.sdk = sdk
if (this.sdk) {
this.name_map = this.sdk.proj.name_map
this.epsg_map = this.sdk.proj.epsg_map
this.convert = this.sdk.proj.convert
this.getAllProjection = this.sdk.proj.getAllProjection
this.projectionIsExists = this.sdk.proj.projectionIsExists
this.degreesToDMS = this.sdk.proj.degreesToDMS
this.dmsToDecimal = this.sdk.proj.dmsToDecimal
}
this.options = { ...options }
}
get POST() {
return 'POST'
}
get GET() {
return 'GET'
}
static _md5(text) {
return md5(text).toUpperCase()
}
/**
* @description 将角度转换为弧度
* @method degreesToRadians
* @param degrees {number} 弧度制
* @return radians {number}
* @memberOf Tools
* @example let radians = tool.degreesToRadians(20)
* */
degreesToRadians(degrees) {
return turf.degreesToRadians(Number(degrees)) // 1.0471975511965976
}
/**
* @description 将弧度转换为角度
* @method radiansToDegrees
* @param radians {number} 弧度制
* @return degrees {number}
* @memberOf Tools
* @example let degrees = tool.radiansToDegrees(2)
* */
radiansToDegrees(radians) {
return turf.radiansToDegrees(Number(radians)) // 1.0471975511965976
}
/**
* @desc 随机字符串
* @function randomString
* @memberOf extra
* @param {number} [e=32] 字符串长度
* @returns {string} str 字符串
* @memberOf Tools
* @example let strings=tool.randomString()
* */
randomString(e) {
/* e = e || 32
var t = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678',
a = t.length,
n = ''
for (let i = 0; i < e; i++) n += t.charAt(Math.floor(Math.random() * a))*/
return Cesium.createGuid()
}
getSourceRootPath() {
let sdkName = 'YJEarth.min.js'
let scripts = document.querySelectorAll('script')
let prefix = ''
scripts.forEach((item) => {
if (item.src && item.src.indexOf(sdkName) > -1) {
let arr = item.src.split('/')
arr.pop()
prefix = arr.join('/')
}
})
return prefix
}
/**@description
* @method cartesian3Towgs84
* @param cartesian {Cesium.Cartesian3} 笛卡尔坐标
* @param viewer
* @param cartesian.x {number}
* @param cartesian.y {number}
* @param cartesian.z {number}
* @memberOf Tools
* @return {object} wgs84 返回wgs84坐标
* */
cartesian3Towgs84(cartesian, viewer) {
var ellipsoid = viewer.scene.globe.ellipsoid
var cartesian3 = new Cesium.Cartesian3(
cartesian.x,
cartesian.y,
cartesian.z
)
var cartographic = ellipsoid.cartesianToCartographic(cartesian3)
var lat = Cesium.Math.toDegrees(cartographic.latitude)
var lng = Cesium.Math.toDegrees(cartographic.longitude)
var alt = cartographic.height < 0 ? 0 : cartographic.height
2025-07-14 09:27:46 +08:00
// var alt = cartographic.height
2025-07-03 13:54:01 +08:00
return {
lng: lng,
lat: lat,
alt: alt,
}
}
/**
* @desc 计算多线段长度
* @method computeDistance
* @memberOf Tools
*@param positions {Array.<{lng:number, lat:number,alt:number}>}坐标数组
* @param [fractionDigits=2] 保留小数点位数
* @param [ground=true] 是否贴地
* */
2025-07-10 16:55:46 +08:00
// computeDistance(positions = [], fractionDigits = 2, ground = true) {
// if (positions.length < 2) {
// return 0
// } else {
// let length = 0
// if (ground) {
// let lineString = []
// positions.forEach((item) => {
// lineString.push([item.lng, item.lat, item.alt])
// })
// let line = turf.lineString(lineString)
// length = turf.length(line) * 1000
// }
// else {
// for (let i = 0; i < positions.length - 1; i++) {
// const position1 = Cesium.Cartesian3.fromDegrees(positions[i].lng, positions[i].lat, positions[i].alt);
// const position2 = Cesium.Cartesian3.fromDegrees(positions[i + 1].lng, positions[i + 1].lat, positions[i + 1].alt);
// const distance = Cesium.Cartesian3.distance(position1, position2);
// length = length + distance
// }
// }
// return length.toFixed(fractionDigits)
// }
// }
async computeDistance(positions = [], fractionDigits = 2, type = '空间长度') {
2025-07-03 13:54:01 +08:00
if (positions.length < 2) {
return 0
} else {
let length = 0
2025-07-10 16:55:46 +08:00
switch (type) {
case '空间长度':
for (let i = 0; i < positions.length - 1; i++) {
const position1 = Cesium.Cartesian3.fromDegrees(positions[i].lng, positions[i].lat, positions[i].alt);
const position2 = Cesium.Cartesian3.fromDegrees(positions[i + 1].lng, positions[i + 1].lat, positions[i + 1].alt);
const distance = Cesium.Cartesian3.distance(position1, position2);
length = length + distance
}
break
case '投影长度':
for (let i = 0; i < positions.length - 1; i++) {
const position1 = Cesium.Cartesian3.fromDegrees(positions[i].lng, positions[i].lat, 0);
const position2 = Cesium.Cartesian3.fromDegrees(positions[i + 1].lng, positions[i + 1].lat, 0);
const distance = Cesium.Cartesian3.distance(position1, position2);
length = length + distance
}
break
case '地表长度':
let meters
let lineString2 = []
positions.forEach((item) => {
lineString2.push([item.lng, item.lat, item.alt])
})
let line2 = turf.lineString(lineString2)
let d = turf.length(line2) * 1000
meters = d > 20 ? d / 20 : d
let res = this.chunkLine(positions, meters)
let coordinates = []
res.forEach((Feature, index) => {
if (index === 0) {
coordinates = [...Feature.geometry.coordinates]
} else {
coordinates.push(Feature.geometry.coordinates[1])
}
})
let arr = []
for (const item of coordinates) {
const index = coordinates.indexOf(item);
let r = await this.sampleHeight({ lng: item[0], lat: item[1], alt: 0 }, index)
r.position.alt = r.position.alt < 0 ? 0 : r.position.alt
arr.push(r)
}
let l = arr.length - 1
for (let i = 0; i < arr.length - 1; i++) {
const position1 = Cesium.Cartesian3.fromDegrees(arr[i].position.lng, arr[i].position.lat, 0);
const position2 = Cesium.Cartesian3.fromDegrees(arr[i + 1].position.lng, arr[i + 1].position.lat, 0);
const distance = Cesium.Cartesian3.distance(position1, position2);
length = length + distance
}
2025-07-10 16:55:46 +08:00
break
default:
break;
2025-07-03 13:54:01 +08:00
}
return length.toFixed(fractionDigits)
}
}
2025-07-10 16:55:46 +08:00
async sampleHeight(p1, index) {
let p2 = await this.sampleHeightMostDetailed([p1])
p1.alt = p2[0].height
return { position: p1, index }
}
2025-07-03 13:54:01 +08:00
/**@description
* @method computeArea
* @memberOf Tools
* @param positions {Array.<{lng:number, lat:number,alt:number}>}
* @param [fractionDigits=2] {number} 精确到小数点多少位
* @return {number} 面积
* */
computeArea(positions, fractionDigits = 2) {
if (positions.length < 3) {
return 0
} else {
let p = []
positions.forEach((item) => {
p.push([item.lng, item.lat])
})
p.push(p[0])
let polygon = turf.polygon([p])
let triangles = turf.tesselate(polygon);
return Number(turf.area(triangles).toFixed(fractionDigits))
}
}
computeSignedArea(viewer, path) {
let _this = this
let fromDegreesArray = []
let fromDegreesArray2 = []
for (let i = 0; i < path.length; i++) {
fromDegreesArray.push(path[i].lng, path[i].lat)
fromDegreesArray2.push([path[i].lng, path[i].lat])
}
let line = turf.lineString(fromDegreesArray2);
let bbox = turf.bbox(line);
let longRadians = Cesium.Math.toRadians(bbox[2] - bbox[0]);
let latRadians = Cesium.Math.toRadians(bbox[3] - bbox[1]);
let granularity
if (longRadians > latRadians) {
granularity = longRadians / 10
}
else {
granularity = latRadians / 10
}
// let granularity = Math.PI / Math.pow(2, 11);
// granularity = granularity / 1250;
let polygonGeometry = new Cesium.PolygonGeometry.fromPositions({
positions: Cesium.Cartesian3.fromDegreesArray(fromDegreesArray),
vertexFormat: Cesium.PerInstanceColorAppearance.FLAT_VERTEX_FORMAT,
granularity: granularity,
});
//创建自定义平面几何体
let geom = new Cesium.PolygonGeometry.createGeometry(polygonGeometry);
if (!geom || !geom.attributes || !geom.attributes.position) {
return 0
}
const indices = geom.indices; //获取顶点索引数据
const positions = geom.attributes.position.values;
function returnPosition(position, index) {
let pos3 = new Cesium.Cartesian3(
positions[index * 3],
positions[index * 3 + 1],
positions[index * 3 + 2]
);
let position1 = viewer.scene.clampToHeight(pos3) || pos3
// let pos84 = await _this.cartesian3Towgs84(position1, viewer)
// let height = 0
// try {
// let position2 = await Cesium.sampleTerrainMostDetailed(
// viewer.scene.terrainProvider,
// [Cesium.Cartographic.fromCartesian(pos3)]
// )
// height = position2[0].height
// } catch (error) {
// height = 0
// }
// if (height > pos84.alt) {
// pos84.alt = height
// }
return position1
}
let area = 0
for (let index = 0; index < indices.length; index += 3) {
const pos0 = returnPosition(positions, indices[index]);
const pos1 = returnPosition(positions, indices[index + 1]);
const pos2 = returnPosition(positions, indices[index + 2]);
// let entity = viewer.entities.add({
// name: "三角面",
// polygon: {
// hierarchy: [pos0, pos1, pos2],
// perPositionHeight: true,
// material: Cesium.Color.fromRandom(),
// outline: true,
// outlineColor: Cesium.Color.BLACK,
// },
// });
let v0 = Cesium.Cartesian3.subtract(pos0, pos1, new Cesium.Cartesian3())
let v1 = Cesium.Cartesian3.subtract(pos2, pos1, new Cesium.Cartesian3())
let cross = Cesium.Cartesian3.cross(v0, v1, v0);
area = (area + Cesium.Cartesian3.magnitude(cross) * 0.5)
}
return Number(area.toFixed(2))
}
/**
* @desc 创建圆
*@method createCircle
* @param point {object} 圆心坐标
* @param point.lng {number} 经度
* @param point.lat {number} 纬度
* @param radius {number} 半径 单位
* @param [options]
* @param [options.steps=360] {number} 段数
* @memberOf Tools
* */
createCircle(point, radius = 1, options = { steps: 360 }) {
if (Number(radius) <= 0) {
radius = 0.01
}
var center = [Number(point.lng), Number(point.lat)]
let coordinates = turf.circle(center, radius / 1000, options).geometry
.coordinates[0]
let positions = []
coordinates.forEach((item) => {
positions = positions.concat(item)
})
return positions
}
/**
* @desc 创建椭圆
*@method createEllipse
* @param point {object} 圆心坐标
* @param point.lng {number} 经度
* @param point.lat {number} 纬度
* @param [options]
* @param [options.granularity=0.1] {number} 椭圆上点之间的角度
* @param [options.semiMinorAxis=10] {number} 短半轴长度 单位
* @param [options.semiMajorAxis=20] {number} 长半轴长度 单位
* @param [options.bearing=0] {number} 椭圆旋转角度 单位
* @memberOf Tools
* */
createEllipse(point, options = { granularity: 0.1, semiMinorAxis: 10, semiMajorAxis: 20, bearing: 0, height: 0 }) {
if (!options.granularity) {
options.granularity = 0.1
}
if (!options.height) {
options.height = 0
}
if (!options.semiMinorAxis) {
options.semiMinorAxis = 0.00001
}
if (!options.semiMajorAxis) {
options.semiMajorAxis = 0.00001
}
let ellipseGeometry = new Cesium.EllipseOutlineGeometry({
center: Cesium.Cartesian3.fromDegrees(Number(point.lng), Number(point.lat)),
semiMajorAxis: Number(options.semiMajorAxis),
semiMinorAxis: Number(options.semiMinorAxis),
height: Number(options.height),
granularity: Cesium.Math.toRadians(options.granularity),
rotation: Cesium.Math.toRadians(options.bearing),
})
let ellipse = Cesium.EllipseOutlineGeometry.createGeometry(ellipseGeometry)
if (!ellipse || !ellipse.attributes || !ellipse.attributes.position) {
return []
}
const ellipsePositions = ellipse.attributes.position.values;
let positions = []
for (let i = 0; i < ellipsePositions.length; i += 3) {
let pos = new Cesium.Cartesian3(
ellipsePositions[i],
ellipsePositions[i + 1],
ellipsePositions[i + 2]
)
positions.push(pos)
}
return positions
}
/*计算2点的恒相线夹角*/
rhumbBearing(p1, p2) {
var point1 = turf.point([p1.lng, p1.lat],);
var point2 = turf.point([p2.lng, p2.lat],);
return turf.rhumbBearing(point1, point2);
}
computeCenter(positions = []) {
let arr = []
positions.forEach((item) => {
arr.push(turf.point([item.lng, item.lat]))
})
var features = turf.featureCollection(arr)
var coordinates = turf.center(features).geometry.coordinates
return {
lng: coordinates[0],
lat: coordinates[1],
}
}
//计算2点的中心点
computeMidpoint(point1, point2) {
let p1 = turf.point([point1.lng, point1.lat])
let p2 = turf.point([point2.lng, point2.lat])
var center = turf.midpoint(p1, p2).geometry.coordinates
return { lng: center[0], lat: center[1] }
}
//计算
computeAttackArrow(positions = []) {
return this.computeArrow(positions, 0)
}
//计算钳击箭头
computePincerArrow(positions = []) {
return this.computeArrow(positions)
}
//计算攻击箭头
computeArrow(positions = [], type = 1) {
if (positions.length < 3) {
return []
} else {
var lnglatArr = []
// if(positions.every(pos => Number(pos.lng) >= 0) || positions.every(pos => Number(pos.lng) <= 0)) {
// for (var i = 0; i < positions.length; i++) {
// lnglatArr.push([positions[i].lng, positions[i].lat])
// }
// }
// else {
// for (var i = 0; i < positions.length; i++) {
// if(positions[i].lng<0) {
// lnglatArr.push([positions[i].lng+360, positions[i].lat])
// }
// else {
// lnglatArr.push([positions[i].lng, positions[i].lat])
// }
// }
// }
for (var i = 0; i < positions.length; i++) {
lnglatArr.push([positions[i].lng, positions[i].lat])
}
var res
if (type) {
res = xp.algorithm.doubleArrow(lnglatArr)
} else {
res = xp.algorithm.tailedAttackArrow(lnglatArr)
}
var index = JSON.stringify(res.polygonalPoint).indexOf('null')
var returnData = []
if (index === -1) returnData = res.polygonalPoint
return returnData
}
}
// 计算集结地
computeAssemble(positions = [], is84 = false) {
if (positions.length < 2) {
return []
} else {
let points = positions.length;
let pnts = new Array();
positions.forEach((item) => {
pnts.push([item.lng, item.lat]);
});
//console.log("pnts6666",pnts);
// pnts.push(tailPoint);
// pnts.push(headerPoint);
if (pnts.length === 2) {
let mid = P.PlotUtils.mid(pnts[0], pnts[1])
//let d = utils.MathDistance(pnts[0], mid) / 0.9
let d = P.PlotUtils.distance(pnts[0], mid) / 0.9
//console.log("d",d);
let pnt = P.PlotUtils.getThirdPoint(pnts[0], mid, P.Constants.HALF_PI, d, true)
pnts = [pnts[0], pnt, pnts[1]];
//console.log("pnt",pnt);
//createPoint(Cesium.Cartesian3.fromDegrees(pnt[0], pnt[1]));
}
let mid = P.PlotUtils.mid(pnts[0], pnts[2])
pnts.push(mid, pnts[0], pnts[1])
let [normals, pnt1, pnt2, pnt3, result, result2] = [[], undefined, undefined, undefined, [], []]
for (let i = 0; i < pnts.length - 2; i++) {
pnt1 = pnts[i]
pnt2 = pnts[i + 1]
pnt3 = pnts[i + 2]
let normalPoints = P.PlotUtils.getBisectorNormals(0.4, pnt1, pnt2, pnt3)
normals = normals.concat(normalPoints)
}
let count = normals.length
normals = [normals[count - 1]].concat(normals.slice(0, count - 1))
for (let i = 0; i < pnts.length - 2; i++) {
pnt1 = pnts[i]
pnt2 = pnts[i + 1]
result = result.concat([...pnt1])
for (let t = 0; t <= P.Constants.FITTING_COUNT; t++) {
let pnt = P.PlotUtils.getCubicValue(t / P.Constants.FITTING_COUNT, pnt1, normals[i * 2], normals[i * 2 + 1], pnt2)
result = result.concat([...pnt])
}
result = result.concat([...pnt2])
}
if (is84) {
return result
}
else {
return Cesium.Cartesian3.fromDegreesArray(result)
}
}
}
// 计算直线箭头
computeStraightArrow(positions = [], is84 = false) {
if (positions.length < 2) {
return []
}
let point1 = [positions[0].lng, positions[0].lat]
let point2 = [positions[1].lng, positions[1].lat]
if (positions[0].lng === positions[1].lng && positions[0].lat === positions[1].lat) {
if (is84) {
return [positions[0], positions[0], positions[0]]
}
else {
let cartesian3 = Cesium.Cartesian3.fromDegrees(positions[0].lng, positions[0].lat)
return [cartesian3, cartesian3, cartesian3]
}
}
let lnglatArr = []
for (let i = 0; i < positions.length; i++) {
lnglatArr.push([positions[i].lng, positions[i].lat])
}
let res
res = xp.algorithm.fineArrow(point1, point2)
if (is84) {
let pos84 = []
for (let i = 0; i < res.length; i++) {
let pos = this.cartesian3Towgs84(res[i], this.sdk.viewer)
pos84.push(pos.lng, pos.lat)
}
return pos84
}
return res
}
2025-07-07 16:21:01 +08:00
getMaterial(color = '#2ab0c2', type = 0, entity = null, newParam = {}) {
2025-07-03 13:54:01 +08:00
let material = ''
2025-07-07 16:21:01 +08:00
2025-07-03 13:54:01 +08:00
switch (Number(type)) {
case 1: //虚线
material = new Cesium.PolylineDashMaterialProperty({
color: Cesium.Color.fromCssColorString(color),
dashLength: 10, //短划线长度
})
break
case 2: //泛光
material = new Cesium.PolylineGlowMaterialProperty({
glowPower: 0.2,
color: Cesium.Color.fromCssColorString(color),
})
break
2025-07-03 16:28:48 +08:00
case 3: //尾迹光线
material = new Cesium.PolylineFlowMaterialProperty({
2025-07-07 16:21:01 +08:00
color: color,
speed: newParam.rotate ? newParam.speed : 0 - newParam.speed,
rotate: newParam.rotate
2025-07-03 16:28:48 +08:00
})
break
case 4: //多尾迹光线
material = new Cesium.PolylineFlowMultMaterialProperty({
2025-07-07 16:21:01 +08:00
color: color,
speed: newParam.rotate ? newParam.speed : 0 - newParam.speed,
rotate: newParam.rotate
2025-07-03 16:28:48 +08:00
})
break
case 5: //普通流动虚线
material = new Cesium.FlowDashedLineFlowMaterialProperty({
color: color,
2025-07-07 16:21:01 +08:00
uType: 0,
speed: newParam.rotate ? newParam.speed : 0 - newParam.speed,
// dashSize: newParam.dashSize,
space: newParam.space
2025-07-03 16:28:48 +08:00
})
break
case 6: //流动虚线2
material = new Cesium.FlowDashedLineFlowMaterialProperty({
color: color,
2025-07-07 16:21:01 +08:00
uType: 1,
speed: newParam.rotate ? newParam.speed : 0 - newParam.speed,
// dashSize: newParam.dashSize,
space: newParam.space
2025-07-03 16:28:48 +08:00
})
break
case 7: //流动箭头1
2025-07-07 16:21:01 +08:00
case 8: //流动箭头2
case 9: //流动箭头3
case 10: //流动箭头4
case 11: //流动箭头5
case 12: //流动箭头6
let param = {
2025-07-03 16:28:48 +08:00
color: color,
2025-07-07 16:21:01 +08:00
image: this.getSourceRootPath() + `/img/arrow/${type - 6}.png`,
space: newParam.space,
speed: newParam.speed
}
2025-07-14 11:32:25 +08:00
let arr = [7, 8, 9, 10, 11]
2025-07-07 16:21:01 +08:00
param.speed = newParam.rotate ? param.speed : 0 - param.speed
this.getFlowTexture(this, param, entity)
2025-07-03 16:28:48 +08:00
break
2025-07-03 13:54:01 +08:00
default:
material = Cesium.Color.fromCssColorString(color)
break
}
return material
}
2025-07-07 16:21:01 +08:00
getFlowTexture(that, options, entity) {
const canvasEle = document.createElement('canvas');
const ctx = canvasEle.getContext('2d')
const myImg = new Image()
// myImg.src = that.getSourceRootPath() + '/img/arrow/1.png'
myImg.src = options.image
myImg.onload = function () {
2025-07-09 11:26:33 +08:00
options.space = Math.max(0.1, options.space);
2025-07-07 16:21:01 +08:00
if (options.speed > 0 || options.speed == 0) {
canvasEle.width = myImg.width * (options.space + 1)
canvasEle.height = myImg.height
ctx.drawImage(myImg, myImg.width * (options.space / 2), 0)
} else {
ctx.clearRect(0, 0, canvasEle.width, canvasEle.height);
canvasEle.width = myImg.width * (options.space + 1)
canvasEle.height = myImg.height
2025-07-09 11:26:33 +08:00
ctx.save(); // 保存当前状态
2025-07-07 16:21:01 +08:00
ctx.translate(canvasEle.width / 2, canvasEle.height / 2); // 移动原点至中心
ctx.rotate(Math.PI); // (弧度制)
2025-07-07 17:17:40 +08:00
ctx.translate(-canvasEle.width / 2, -canvasEle.height / 2); // 移回原点
2025-07-09 11:26:33 +08:00
ctx.drawImage(myImg, myImg.width * (options.space / 2), 0)
2025-07-07 16:21:01 +08:00
ctx.restore(); // 恢复状态
}
// let repeat = getRepeat()
// }, false)
entity.polyline.material = new Cesium.LineTextureMaterialProperty(
{
color: options.color,
// image: options.image,
image: canvasEle,
speed: options.speed,
// repeat: repeat
repeat: new Cesium.CallbackProperty(function () {
// function getRepeat() {
var positionProperty = entity.polyline.positions;
var positions = positionProperty.getValue(that.sdk.viewer.clock.currentTime);
if (!Cesium.defined(positions)) {
// return new Cesium.Cartesian2(1.0, 1.0);
return 1.0;
}
var distance = 0;
for (var i = 0; i < positions.length - 1; ++i) {
distance += Cesium.Cartesian3.distance(positions[i], positions[i + 1]);
}
var repeatX = distance / entity.polyline.width.getValue();
// 根据地图缩放程度调整repeatX
var cameraHeight = that.sdk.viewer.camera.positionCartographic.height;
var boundingSphere = new Cesium.BoundingSphere(
new Cesium.Cartesian3(-1000000, 0, 0), // 中心点坐标
500000 // 半径(距离)
);
// 获取绘图缓冲区的宽度和高度(通常是屏幕的分辨率)
var drawingBufferWidth = that.sdk.viewer.canvas.clientWidth;
var drawingBufferHeight = that.sdk.viewer.canvas.clientHeight;
// 使用 getPixelSize 方法获取包围球在屏幕上的像素大小
var groundResolution = that.sdk.viewer.scene.camera.getPixelSize(boundingSphere, drawingBufferWidth, drawingBufferHeight)
// repeatX *= groundResolution / cameraHeight / ((myImg.width / myImg.height * 5) + 1);
repeatX *= groundResolution / cameraHeight / (options.space * (canvasEle.width / canvasEle.height * 5) + 1);
// if (repeatX < 3) {
// repeatX = 3
// }
// return new Cesium.Cartesian2(repeatX, 1.0);
return repeatX;
})
}
)
}
}
2025-07-03 13:54:01 +08:00
/*创建直箭头图片*/
create_arrow1_picture(color) {
let canvas = new fabric.Canvas('canvas')
canvas.width = 150
canvas.height = 75
let w_offset = 35
let h_offset = 20
let fill = color
let polygon = new fabric.Polygon(
[
{ x: canvas.width, y: canvas.height / 2 }, //箭头右边顶点
{ x: canvas.width - w_offset, y: canvas.height }, //顺时针
{ x: canvas.width - w_offset, y: canvas.height - h_offset }, //
{ x: canvas.width / 2, y: canvas.height - h_offset }, //
{ x: canvas.width / 2, y: h_offset }, //
{ x: canvas.width - w_offset, y: h_offset }, //
{ x: canvas.width - w_offset, y: 0 }, //
],
{
fill,
}
)
canvas.add(polygon)
return canvas.toDataURL()
}
/*创建箭头图片*/
create_arrow2_picture(color) {
let canvas = new fabric.Canvas('canvas')
canvas.width = 75
canvas.height = 75
let w_offset1 = 35
let w_offset2 = 40
let fill = color
let polygon = new fabric.Polygon(
[
{ x: canvas.width, y: canvas.height / 2 },
{ x: canvas.width - w_offset1, y: canvas.height },
{ x: canvas.width - w_offset1 - w_offset2, y: canvas.height },
{ x: canvas.width - w_offset2, y: canvas.height / 2 },
{ x: canvas.width - w_offset1 - w_offset2, y: 0 },
{ x: canvas.width - w_offset1, y: 0 },
],
{
fill,
}
)
canvas.add(polygon)
return canvas.toDataURL()
}
sampleHeightMostDetailed(positions) {
let cartesians = []
positions.forEach((item) => {
cartesians.push(new Cesium.Cartographic.fromDegrees(item.lng, item.lat))
})
return this.sdk.viewer.scene.sampleHeightMostDetailed(cartesians)
}
flyHome(duration = 3) {
setActiveViewer(0)
closeRotateAround(this.sdk)
closeViewFollow(this.sdk)
if (this.sdk.viewer.CAMERA_DEFAULT_VIEW_RECTANGLE) {
let destination = this.sdk.viewer.CAMERA_DEFAULT_VIEW_RECTANGLE.destination
let orientation = this.sdk.viewer.CAMERA_DEFAULT_VIEW_RECTANGLE.orientation
this.sdk.viewer.camera.flyTo({
destination: Cesium.Cartesian3.fromDegrees(destination.lng, destination.lat, destination.alt),
orientation: {
heading: Cesium.Math.toRadians(orientation.heading || 0),
pitch: Cesium.Math.toRadians(orientation.pitch || 0),
roll: Cesium.Math.toRadians(orientation.roll || 0)
},
duration: duration
})
}
else {
this.sdk.viewer.camera.flyHome(duration)
}
}
/**
* @description 设置拾取操作启停
* @method setPickStatus
* @param [status=true] {boolean}
* @memberOf Tools
* */
setPickStatus(status = true) {
// YJ.getEarth().interaction.picking.enabled = status
}
/**
* @description 创建点缓冲区 返回缓冲区坐标
* @method create_point_buffer
* @param point {object} 坐标参数
* @param [radius=5] {number} 缓冲距离 单位
* @param option
* @param point.lng {number} 经度
* @param point.lat {number} 纬度
* @memberOf Tools
* @returns {Array} 返回缓冲区坐标数组
*/
create_point_buffer(point, radius = 5, option = { steps: 10 }) {
let p = turf.point([Number(point.lng), Number(point.lat)])
let buffered = turf.buffer(p, Number(radius) / 1000, option)
return buffered.geometry.coordinates
}
/**
* @description 创建线缓冲区 返回缓冲区坐标
* @method create_line_buffer
* @param points {Array.<lng:"",lat:"">} 坐标参数
* @param [radius=5] {number} 缓冲距离 单位
* @memberOf Tools
* @returns {Array} 返回缓冲区坐标数组
* @example let points = [{lng:"",lat:""},{lng:"",lat:""},{lng:"",lat:""}]
* let radius = 10
* let area_buffer = sdk.create_line_buffer(points,radius)
* */
create_line_buffer(points, radius = 5) {
if (points.length === 1) {
return this.create_point_buffer(points[0], radius)
} else if (points.length === 0) {
return false
} else {
let arr = []
points.forEach((point) => {
arr.push([Number(point.lng), Number(point.lat)])
})
let line = turf.lineString(arr)
let buffered = turf.buffer(line, Number(radius) / 1000)
return buffered.geometry.coordinates
}
}
/**
* @description 创建面缓冲区 返回缓冲区坐标
* @method create_area_buffer
* @param points {Array.<lng:"",lat:"">} 坐标参数
* @param [radius=5] {number} 缓冲距离 单位
* @memberOf Tools
* @returns {Array} 返回缓冲区坐标数组
* @example let points = [{lng:"",lat:""},{lng:"",lat:""},{lng:"",lat:""}]
* let radius = 10
* let area_buffer = sdk.create_area_buffer(points,radius)
* */
create_area_buffer(points, radius = 5) {
if (points.length === 1) {
return this.create_point_buffer(points[0], radius)
} else if (points.length === 0) {
return false
} else if (points.length === 2) {
return this.create_line_buffer(points, radius)
} else {
let arr = []
points.forEach((point) => {
arr.push([Number(point.lng), Number(point.lat)])
})
arr.push(arr[0])
let poly = turf.polygon([arr])
let buffered = turf.buffer(poly, Number(radius) / 1000)
return buffered.geometry.coordinates
}
}
/**
* @desc 计算多点范围
* @method cal_envelope
* @memberOf Tools
* */
cal_envelope(points = []) {
if (points.length < 2) {
console.error('坐标数量不能少于2个')
} else {
let arr = []
points.forEach((p) => {
arr.push(turf.point([p.lng, p.lat]))
})
let features = turf.featureCollection(arr)
return turf.envelope(features).geometry.coordinates[0]
}
}
/**
* @desc 按指定距离分段
* @method chunkLine
* @param positions {Array}
* @param meters
* @memberOf Tools
* */
chunkLine(positions = [], meters = 1000) {
if (positions.length < 2) {
return []
} else {
let arr = []
positions.forEach((it) => {
arr.push([it.lng, it.lat])
})
let line = turf.lineString(arr)
let chunk = turf.lineChunk(line, meters / 1000)
let __map = new Map()
chunk.features.forEach((value) => {
value.geometry.coordinates.forEach((value1) => {
let key = value1.join(',')
let val = { lng: value1[0], lat: value1[1] }
__map.set(key, val)
})
})
return chunk.features
}
}
/**
* @desc 标注以sinx的半个周期的形式弹跳
* */
billboardAnimation(viewer, { id, offset = 10, times = 3 }) {
let entity = viewer.entities.getById(id)
if (entity) {
let pi = Math.PI
let tt = 60
let of = pi / tt
let h = 0
let src_p = this.cartesian3Towgs84(entity.position.getValue(), viewer)
// let height = src_p.alt
// entity.position = new Cesium.CallbackProperty(() => {
// let height = offset * Math.sin(h) + src_p.alt
// h += of
// return Cesium.Cartesian3.fromDegrees(src_p.lng, src_p.lat, height)
// }, false)
let ti = setInterval(() => {
h += of
let height = offset * Math.sin(h) + src_p.alt
if (h > pi) h = 0
entity.position = Cesium.Cartesian3.fromDegrees(
src_p.lng,
src_p.lat,
height
)
}, 30)
setTimeout(() => {
clearInterval(ti)
entity.position = Cesium.Cartesian3.fromDegrees(
src_p.lng,
src_p.lat,
src_p.alt
)
}, 3000)
}
}
/**@desc
* @method sector
* @memberOf Tools*/
calSector(
center = {},
radius = 100,
bearing1 = 25,
bearing2 = 45,
steps = 360,
is84 = false
) {
let c = turf.point([center.lng, center.lat])
let coordinates = turf.sector(c, radius / 1000, bearing1, bearing2, { steps: steps })
.geometry.coordinates
let positions = []
coordinates.forEach((array) => {
array.forEach((item) => {
let pos
if (is84) {
pos = {
lng: item[0],
lat: item[1],
}
}
else {
pos = Cesium.Cartesian3.fromDegrees(item[0], item[1])
}
positions.push(pos)
})
})
return positions
}
/**
* @desc获取紧贴高度
* @param position 坐标
*/
async getClampToHeight(position, objectsToExclude = []) {
if (!this.sdk || !this.sdk.viewer) {
return 0
}
let pos3 = new Cesium.Cartesian3.fromDegrees(position.lng, position.lat);
let position1
try {
position1 = await this.sdk.viewer.scene.clampToHeight(pos3, objectsToExclude) || pos3
} catch (error) {
position1 = pos3
}
let height1 = await this.cartesian3Towgs84(position1, this.sdk.viewer).alt
let height2 = 0
try {
if (this.sdk.viewer.scene.terrainProvider.availability) {
let position2 = await Cesium.sampleTerrainMostDetailed(
this.sdk.viewer.scene.terrainProvider,
[Cesium.Cartographic.fromDegrees(position.lng, position.lat)]
)
position2[0].height && (height2 = position2[0].height)
}
} catch (error) {
height2 = 0
}
if (height1 > height2) {
return Number(height1.toFixed(2))
}
else {
return Number(height2.toFixed(2))
}
}
/**
* @desc 深拷贝对象
* */
deepCopyObj(obj) {
let newobj = null;
//判断是否需要继续进行递归
if (typeof (obj) == 'object' && obj !== null) {
newobj = obj instanceof Array ? [] : {}; //进行下一层递归克隆
for (let i in obj) {
if (i != 'earth' && i != 'Dialog')
newobj[i] = this.deepCopyObj(obj[i])
} //如果不是对象直接赋值
} else newobj = obj;
return newobj;
}
/**
* @desc 度分秒转换
* */
projConvert(status = false, closeCallBack = () => { }) {
if (status) {
projConvertOpen(this.sdk, closeCallBack)
}
else {
projConvertClose()
}
}
/**
* @desc 投影转换
* */
projectionConvert(status = false, closeCallBack = () => { }) {
if (status) {
projectionConvertOpen(this.sdk, closeCallBack)
}
else {
projectionConvertClose()
}
}
/**
* @desc 导出excel
*/
// 导出属性
exportExcel(data) {
let label = '<Row>'
for (let key in data[0]) {
label += `<Cell><Data ss:Type="String">${key}</Data></Cell>`
}
label += '</Row>'
let url = 'data:application/vnd.ms-excel;base64,',
tmplWorkbookXML = '<?xml version="1.0"?><?mso-application progid="Excel.Sheet"?><Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet" xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet">'
+ '<DocumentProperties xmlns="urn:schemas-microsoft-com:office:office"><Author>Axel Richter</Author><Created>{created}</Created></DocumentProperties>'
+ '<Styles>'
+ '<Style ss:ID="Currency"><NumberFormat ss:Format="Currency"></NumberFormat></Style>'
+ '<Style ss:ID="Date"><NumberFormat ss:Format="Medium Date"></NumberFormat></Style>'
+ '</Styles>'
+ '{worksheets}</Workbook>'
, tmplWorksheetXML = '<Worksheet ss:Name="{nameWS}"><Table><ss:Column ss:Width="120"/><ss:Column ss:Width="120"/><ss:Column ss:Width="120"/><ss:Column ss:Width="120"/><ss:Column ss:Width="120"/><ss:Column ss:Width="120"/><ss:Column ss:Width="120"/><ss:Column ss:Width="120"/><ss:Column ss:Width="120"/><ss:Column ss:Width="120"/><ss:Column ss:Width="120"/><ss:Column ss:Width="120"/><ss:Column ss:Width="120"/><ss:Column ss:Width="240"/>{rows}</Table></Worksheet>'
, tmplCellXML = '<Cell><Data ss:Type="{nameType}">{data}</Data></Cell>'
, base64 = function (s) {
return window.btoa(unescape(encodeURIComponent(s)))
}
, format = function (s, c) {
return s.replace(/{(\w+)}/g, function (m, p) {
return c[p];
})
}
let ctx = "";
let workbookXML = "";
let worksheetsXML = "";
let rowsXML = "";
let pil = 0;
for (let i = 0; i < data.length; i++) {
if (i == 0) {
rowsXML += label
}
rowsXML += '<Row>';
for (let key in data[i]) {
ctx = {
nameType: 'String',
data: data[i][key]
};
rowsXML += format(tmplCellXML, ctx);
}
rowsXML += '</Row>';
if (i > 0 && (i / 60000) % 1 === 0) {
pil++;
ctx = { rows: rowsXML, nameWS: 'Sheet' + i };
worksheetsXML += format(tmplWorksheetXML, ctx);
rowsXML = "";
rowsXML += label
}
}
ctx = { rows: rowsXML, nameWS: 'Sheet' };
worksheetsXML += format(tmplWorksheetXML, ctx);
rowsXML = "";
ctx = { created: (new Date()).getTime(), worksheets: worksheetsXML };
workbookXML = format(tmplWorkbookXML, ctx);
let link = document.createElement("A");
link.href = url + base64(workbookXML);
link.download = "矢量数据导出.xls"
link.target = '_blank';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
/**
* @desc Gzip解压
*/
decompressGzip(compressedData) {
let decompressedData = pako.inflate(compressedData);
// 将解压缩后的数据转换为字符串
let decompressedString = new TextDecoder().decode(decompressedData);
return decompressedString
}
/**
* @desc cmyk转rbg
2025-07-03 16:28:48 +08:00
* @param {*} color
* @returns
2025-07-03 13:54:01 +08:00
*/
cmykToRgb(color) {
let { c, m, y, k } = color
let rgb = { r: 0, g: 0, b: 0 };
rgb.c = c / 100;
rgb.m = m / 100;
rgb.y = y / 100;
rgb.k = k / 100;
rgb.r = 1 - Math.min(1, rgb.c * (1 - rgb.k) + rgb.k);
rgb.g = 1 - Math.min(1, rgb.m * (1 - rgb.k) + rgb.k);
rgb.b = 1 - Math.min(1, rgb.y * (1 - rgb.k) + rgb.k);
rgb.r = Math.round(rgb.r * 255);
rgb.g = Math.round(rgb.g * 255)
rgb.b = Math.round(rgb.b * 255);
return `rgb(${rgb.r}, ${rgb.g}, ${rgb.b})`;
}
convertPxToRem(sdk, selector) {
const htmlFontSize = parseFloat(getComputedStyle(document.documentElement).fontSize);
let list = []
let elementList = []
if (selector) {
list = document.getElementById(sdk.div_id).querySelectorAll(selector)
for (let i = 0; i < list.length; i++) {
elementList.push(list[i], ...list[i].querySelectorAll('*'))
}
}
else {
elementList = document.getElementById(sdk.div_id).querySelectorAll('*');
}
elementList.forEach(element => {
const style = element.style;
const rules = Array.from(style).filter(prop => {
return typeof prop === 'string' && prop.endsWith('px');
});
rules.forEach(prop => {
const pxValue = parseFloat(style[prop]);
const remValue = pxValue / htmlFontSize;
element.style[prop] = `${remValue}rem`;
});
});
}
isConvex(arr = []) {
const { length } = arr
let pre = 0,
curr = 0
for (let i = 0; i < length; ++i) {
let dx1 = arr[(i + 1) % length].lng - arr[i].lng
let dx2 = arr[(i + 2) % length].lng - arr[(i + 1) % length].lng
let dy1 = arr[(i + 1) % length].lat - arr[i].lat
let dy2 = arr[(i + 2) % length].lat - arr[(i + 1) % length].lat
curr = dx1 * dy2 - dx2 * dy1
if (curr != 0) {
if ((curr > 0 && pre < 0) || (curr < 0 && pre > 0)) return false
else pre = curr
}
}
return true
}
getDateTimeString() {
// 创建一个表示当前时间的 Date 对象
const now = new Date();
// 获取年份
const year = now.getFullYear();
// 获取月份(从 0 开始,所以要加 1并将其转换为两位字符串不足两位在前面补零
const month = String(now.getMonth() + 1).padStart(2, '0');
// 获取日期,并将其转换为两位字符串,不足两位在前面补零
const day = String(now.getDate()).padStart(2, '0');
// 获取小时,并将其转换为两位字符串,不足两位在前面补零
const hours = String(now.getHours()).padStart(2, '0');
// 获取分钟,并将其转换为两位字符串,不足两位在前面补零
const minutes = String(now.getMinutes()).padStart(2, '0');
// 获取秒,并将其转换为两位字符串,不足两位在前面补零
const seconds = String(now.getSeconds()).padStart(2, '0');
// 获取毫秒,并将其转换为三位字符串,不足三位在前面补零
const milliseconds = String(now.getMilliseconds()).padStart(3, '0');
// 将年、月、日、时、分、秒、毫秒拼接成所需的字符串
return `${year}${month}${day}${hours}${minutes}${seconds}${milliseconds}`;
}
replaceHost(url, host) {
let newUrl = url
if (!url || !host) {
return url
}
try {
if (!url.startsWith("http")) {
//说明是本地的json在磁盘中存在的
if (!url.includes(":")) {
if (this.options.host) {
let o = new URL(url, this.options.host)
newUrl = o.href
}
}
return newUrl
}
else {
// 移除可能的用户名:密码前缀
const authRegex = /^[^@]+@/;
if (authRegex.test(url)) {
url = url.replace(authRegex, '');
}
// 添加协议前缀(如果没有)
if (!/^[a-z]+:\/\//i.test(url)) {
url = 'http://' + url;
}
const parsedUrl = new URL(url);
const parsedUrl2 = new URL(host);
let hostname = parsedUrl.hostname;
let port = parsedUrl.port;
// 处理IPv6地址如果有括号
if (hostname.startsWith('[') && hostname.endsWith(']')) {
hostname = hostname.slice(1, -1);
}
if ((hostname === 'localhost' || hostname === '127.0.0.1') && parseInt(port, 10) !== 55110) {
parsedUrl.port = parsedUrl2.port
parsedUrl.protocol = parsedUrl2.protocol
newUrl = parsedUrl.toString()
}
return newUrl
}
} catch (error) {
return newUrl
}
}
2025-07-03 13:54:01 +08:00
}
export default Tools