/** * @name: index * @author: Administrator * @date: 2022-06-09 16:46 * @description:index * @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] 是否贴地 * */ // 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 = '空间长度') { 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) } } 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 = '' 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 }) break case 6: //流动虚线2 material = new Cesium.FlowDashedLineFlowMaterialProperty({ color: color, uType: 1, speed: newParam.rotate ? newParam.speed : 0 - newParam.speed, // dashSize: newParam.dashSize, space: newParam.space }) 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 = 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; }) } ) } } /*创建直箭头图片*/ 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.} 坐标参数 * @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.} 坐标参数 * @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 = '' for (let key in data[0]) { label += `${key}` } label += '' let url = 'data:application/vnd.ms-excel;base64,', tmplWorkbookXML = '' + 'Axel Richter{created}' + '' + '' + '' + '' + '{worksheets}' , tmplWorksheetXML = '{rows}
' , tmplCellXML = '{data}' , 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 += ''; for (let key in data[i]) { ctx = { nameType: 'String', data: data[i][key] }; rowsXML += format(tmplCellXML, ctx); } rowsXML += ''; 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