Files
sdk4.0/src/Tools/index.js
2025-07-15 18:23:31 +08:00

1404 lines
45 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* @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'
import FlowPictureMaterialProperty from '../Obj/Materail/FlowPictureMaterialProperty'
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
// var alt = cartographic.height
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] 是否贴地
* */
async computeDistance(positions = [], fractionDigits = 2, type = '空间长度') {
if (positions.length < 2) {
return 0
} else {
let length = 0
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
}
break
default:
break;
}
return length.toFixed(fractionDigits)
}
}
computeDistance2(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 sampleHeight(p1, index) {
let p2 = await this.sampleHeightMostDetailed([p1])
p1.alt = p2[0].height
return { position: p1, index }
}
/**@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
}
getMaterial(color = '#2ab0c2', type = 0, entity = null, newParam = {}) {
let material = ''
let arr = {
'7': 10,
'8': 3,
'9': 4,
'10': 4,
'11': 4,
'12': 2
}
if (entity) {
arr[type + ''] ? (entity.polyline.width = entity.polyline.oriWidth + arr[type + '']) : (entity.polyline.width = entity.polyline.oriWidth)
}
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
case 3: //尾迹光线
material = new Cesium.PolylineFlowMaterialProperty({
color: color,
speed: newParam.rotate ? newParam.speed : 0 - newParam.speed,
rotate: newParam.rotate
})
break
case 4: //多尾迹光线
material = new Cesium.PolylineFlowMultMaterialProperty({
color: color,
speed: newParam.rotate ? newParam.speed : 0 - newParam.speed,
rotate: newParam.rotate
})
break
case 5: //普通流动虚线
// material = new Cesium.FlowDashedLineFlowMaterialProperty({
// color: color,
// uType: 0,
// speed: newParam.rotate ? newParam.speed : 0 - newParam.speed,
// // dashSize: newParam.dashSize,
// space: newParam.space,
// scale: 1.0
// })
// break
case 6: //流动虚线2
let that = this
material = new Cesium.FlowDashedLineFlowMaterialProperty({
color: color,
uType: type == 5 ? 0 : 1,
speed: newParam.rotate ? newParam.speed : 0 - newParam.speed,
// dashSize: newParam.dashSize,
space: newParam.space,
scale: new Cesium.CallbackProperty(function () {
var oriPositions = entity.polyline.positions.getValue();
if (!Cesium.defined(oriPositions)) {
return 1.0;
}
var distance = 0;
for (var i = 0; i < oriPositions.length - 1; ++i) {
distance += Cesium.Cartesian3.distance(oriPositions[i], oriPositions[i + 1]);
}
//屏幕坐标
let point1 = new Cesium.Cartesian2(0, that.sdk.viewer.canvas.clientHeight)
let point2 = new Cesium.Cartesian2(that.sdk.viewer.canvas.clientWidth / 2, that.sdk.viewer.canvas.clientHeight)
var cartesian1 = that.sdk.viewer.scene.pickPosition(point1)
var cartesian2 = that.sdk.viewer.scene.pickPosition(point2)
var distance2 = Cesium.Cartesian3.distance(cartesian1, cartesian2);
let repeatX = distance2 * 2 / distance
return repeatX;
})
})
break
case 7: //流动箭头1
case 8: //流动箭头2
case 9: //流动箭头3
case 10: //流动箭头4
case 11: //流动箭头5
case 12: //流动箭头6
let param = {
color: color,
image: this.getSourceRootPath() + `/img/arrow/${type - 6}.png`,
space: newParam.space,
speed: newParam.speed
}
param.speed = newParam.rotate ? param.speed : 0 - param.speed
this.getFlowTexture(this, param, entity)
break
default:
material = Cesium.Color.fromCssColorString(color)
break
}
return material
}
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 () {
options.space = Math.max(0.1, options.space);
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
ctx.save(); // 保存当前状态
ctx.translate(canvasEle.width / 2, canvasEle.height / 2); // 移动原点至中心
ctx.rotate(Math.PI); // (弧度制)
ctx.translate(-canvasEle.width / 2, -canvasEle.height / 2); // 移回原点
ctx.drawImage(myImg, myImg.width * (options.space / 2), 0)
ctx.restore(); // 恢复状态
}
// let repeat = getRepeat()
// }, false)
// entity.polyline.material.oriRepeat = that.getSceenLine(entity, options, canvasEle)
// entity.polyline.material.oriSpeed = undefined
let beforeSpeed = 0, repeat = 0
entity.polyline.material = new Cesium.LineTextureMaterialProperty(
{
color: options.color,
image: canvasEle,
speed: options.speed,
repeat: new Cesium.CallbackProperty(function () {
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)
let result
if (groundResolution > 700) {
repeatX *= groundResolution / cameraHeight / (options.space * (canvasEle.width / canvasEle.height * 5) + 1);
// if (entity.polyline.material.oriRepeat) {
let speed = repeatX / entity.polyline.oriRepeat
entity.polyline.oriSpeed = speed
entity.polyline.oriRepeatX = repeatX
// } else {
// entity.polyline.material.oriRepeat = repeatX
// }
beforeSpeed = speed
repeat = repeatX
result = new Cesium.Cartesian2(repeatX, speed || 1.0)
} else {
result = new Cesium.Cartesian2(repeat || entity.polyline.oriRepeatX, beforeSpeed || entity.polyline.oriSpeed)
}
return result;
// return repeatX;
})
}
)
let oriRepeat = that.getSceenLine(entity, options, canvasEle)
oriRepeat && (entity.polyline.oriRepeat = oriRepeat)
}
}
/**获取当前满屏横线速度 */
getSceenLine(entity, options, canvasEle) {
let point1 = new Cesium.Cartesian2(0, this.sdk.viewer.canvas.clientHeight)
let point2 = new Cesium.Cartesian2(this.sdk.viewer.canvas.clientWidth / 2, this.sdk.viewer.canvas.clientHeight)
var cartesian1 = this.sdk.viewer.scene.pickPosition(point1)
var cartesian2 = this.sdk.viewer.scene.pickPosition(point2)
var distance = Cesium.Cartesian3.distance(cartesian1, cartesian2);
var repeatX = distance / entity.polyline.width.getValue();
// 根据地图缩放程度调整repeatX
var cameraHeight = this.sdk.viewer.camera.positionCartographic.height;
var boundingSphere = new Cesium.BoundingSphere(
new Cesium.Cartesian3(-1000000, 0, 0), // 中心点坐标
500000 // 半径(距离)
);
// 获取绘图缓冲区的宽度和高度(通常是屏幕的分辨率)
var drawingBufferWidth = this.sdk.viewer.canvas.clientWidth;
var drawingBufferHeight = this.sdk.viewer.canvas.clientHeight;
// 使用 getPixelSize 方法获取包围球在屏幕上的像素大小
var groundResolution = this.sdk.viewer.scene.camera.getPixelSize(boundingSphere, drawingBufferWidth, drawingBufferHeight)
// repeatX *= groundResolution / cameraHeight / ((myImg.width / myImg.height * 5) + 1);
if (groundResolution > 700) {
repeatX *= groundResolution / cameraHeight / (options.space * (canvasEle.width / canvasEle.height * 5) + 1);
} else {
repeatX = undefined;
}
return repeatX
}
/*创建直箭头图片*/
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
* @param {*} color
* @returns
*/
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
}
}
}
export default Tools