uavInspection.js代码
- /**
- * 无人机巡检
- */
- import * as mars3d from "mars3d"
- import * as Cesium from 'mars3d-cesium'
- import * as turf from "@turf/turf"
- import { getMap } from '@/components/mars3dMap/js/index.js'
- import { getHeightByLngLat } from '@/components/mars3dMap/js/utils.js'
- import GlowLineMaterialProperty from '@/components/mars3dMap/js/material/GlowLineMaterialProperty.js'
- import iamge from "@/components/mars3dMap/images/路径撒点.png";
- import iamge2 from "@/components/mars3dMap/images/路径撒点2.png";
- let graphicLayer;
- let uavInspectionMap = new Map();
- let pointCallback; // 无人机经过点位回调
- let UavInspection = (function () {
- /**
- * 无人机巡检
- */
- function UavInspection(id, positions) {
- this.id = id;
- this.positions = positions || [];
- this.graphicMap = new Map();
- this.step = 20;
- this.callbackPointSet = new Set(); // 回调点位集合
- if (positions && positions.length > 0) {
- this.segmentPositions = this.divideLine(positions, this.step);
- }
- if (!graphicLayer) {
- let map = getMap();
- graphicLayer = new mars3d.layer.GraphicLayer()
- map.addLayer(graphicLayer);
- }
- }
- UavInspection.prototype.finishLabels = function (index) {
- for (let i = 1; i <= index; i++) {
- this.finishLabel(i);
- }
- }
- UavInspection.prototype.finishLabel = function (index) {
- let id = `label-${index}`;
- if (this.graphicMap.has(id)) {
- let data = this.graphicMap.get(id);
- data.graphic.setStyle({ image: iamge2 });
- }
- }
- UavInspection.prototype.addLabels = function () {
- for (let [i, pos] of this.positions.entries()) {
- let samePos = false; // 第1个点和最后一个点位置是否相同
- if (this.positions.length > 0) {
- let lastPos1 = this.positions[0];
- let lastPos2 = this.positions[this.positions.length - 1];
- let distance = Cesium.Cartesian3.distance(Cesium.Cartesian3.fromDegrees(lastPos1[0], lastPos1[1], lastPos1[2]),
- Cesium.Cartesian3.fromDegrees(lastPos2[0], lastPos2[1], lastPos2[2]));
- if (distance < 5) {
- samePos = true;
- }
- }
- if (i < (samePos ? this.positions.length - 1 : this.positions.length)) {
- this.addLabel(i + 1, pos[0], pos[1], pos[2]);
- }
- }
- }
- UavInspection.prototype.addLabel = function (index, lng, lat, height) {
- let id = `label-${index}`;
- let data;
- if (this.graphicMap.has(id)) {
- return;
- } else {
- data = { id: id, type: 'label' };
- this.graphicMap.set(id, data);
- }
- data.graphic = new mars3d.graphic.BillboardEntity({
- id: id,
- position: [lng, lat, height],
- style: {
- image: iamge,
- scale: 0.5,
- horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
- verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
- clampToGround: false,
- pixelOffsetY: 30,
- label: {
- text: `${index}`,
- pixelOffsetY: -31,
- visibleDepth: false
- }
- },
- })
- graphicLayer.addGraphic(data.graphic);
- }
- UavInspection.prototype.clear = function () {
- for (let id of this.graphicMap.keys()) {
- let data = this.graphicMap.get(id);
- this.graphicMap.delete(id);
- graphicLayer.removeGraphic(data.graphic);
- data.graphic = undefined;
- if (data.type == 'drone') {
- data.positionProperty = undefined;
- }
- }
- this.callbackPointSet.clear();
- }
- UavInspection.prototype.createPath = function () {
- return { // 实时轨迹显示
- show: true,
- leadTime: 0, // 飞机将要经过的路径,路径存在的时间
- trailTime: 60, // 飞机已经经过的路径,路径存在的时间
- width: 8, // 线宽度
- resolution: 1,
- // color: 'rgba(255, 193, 37, 1)',
- /* material: new Cesium.PolylineGlowMaterialProperty({
- glowPower: 0.25, // 轨迹线的发光强度
- color: new Cesium.Color(255 / 255, 193 / 255, 37 / 255, 1) // 颜色
- }) */
- material: new GlowLineMaterialProperty({
- color: new Cesium.Color(255 / 255, 255 / 255, 0 / 255, 1),
- power: 0.2
- })
- };
- }
- UavInspection.prototype.createModel = function () {
- return new mars3d.graphic.ModelEntity({
- position: [0, 0, 0], // 默认值
- style: {
- url: 'gltf/四旋翼无人机1.glb',
- scale: 6,
- minimumPixelSize: 100,
- heading: 0
- },
- path: this.createPath()
- });
- }
- UavInspection.prototype.createDrone = function () {
- let id = this.id;
- let data;
- if (this.graphicMap.has(id)) {
- return;
- } else {
- data = { id: id, type: 'drone' };
- this.graphicMap.set(id, data);
- data.positionProperty = new Cesium.SampledPositionProperty();
- data.positionProperty.forwardExtrapolationType = Cesium.ExtrapolationType.HOLD; // 后续时段保持末位置
- }
- if (!data.graphic) {
- data.graphic = this.createModel();
- graphicLayer.addGraphic(data.graphic);
- data.graphic.position = data.positionProperty;
- this.addDashLine();
- }
- }
- UavInspection.prototype.updateDronePosition = function (lng, lat, height) {
- let id = this.id;
- if (this.graphicMap.has(id)) {
- let data = this.graphicMap.get(id);
- if (data.graphic) {
- this.lastTime = new Date().getTime();
- data.position = [lng, lat, height];
- let position = Cesium.Cartesian3.fromDegrees(lng, lat, height);
- let delay = 2000; // 延迟(单位:毫秒)
- let time = Cesium.JulianDate.fromDate(new Date(new Date().getTime() + delay));
- data.positionProperty.addSample(time, position);
- setTimeout(() => {
- this.updateDashLine(lng, lat, height);
- this.drawBlocks(lng, lat);
- }, delay);
- }
- }
- // 无人机经纬标签时标签变色
- for (let [i, pos] of this.positions.entries()) {
- if (i < this.positions.length) {
- let distance = Cesium.Cartesian3.distance(Cesium.Cartesian3.fromDegrees(lng, lat, 0), Cesium.Cartesian3.fromDegrees(pos[0], pos[1], 0));
- if (distance < 10) {
- this.finishLabels(i + 1);
- }
- }
- }
- // 指定点位回调
- for (let [i, pos] of this.positions.entries()) {
- let distance = Cesium.Cartesian3.distance(Cesium.Cartesian3.fromDegrees(lng, lat, 0), Cesium.Cartesian3.fromDegrees(pos[0], pos[1], 0));
- if (distance < 10) {
- if (pointCallback && !this.callbackPointSet.has(i + 1)) {
- let map = getMap();
- const screenPoint = map.scene.cartesianToCanvasCoordinates(Cesium.Cartesian3.fromDegrees(lng, lat, height));
- this.callbackPointSet.add(i + 1);
- setTimeout(() => {
- pointCallback(i + 1, screenPoint);
- }, 100);
- }
- }
- }
- }
- UavInspection.prototype.addDashLine = function () {
- let id = `dashline-${this.id}`;
- let data;
- if (this.graphicMap.has(id)) {
- return;
- } else {
- data = { id: id, type: 'dashline' };
- this.graphicMap.set(id, data);
- }
- if (!data.graphic) {
- data.graphic = new mars3d.graphic.PolylineEntity({
- positions: undefined,
- style: {
- width: 2,
- clampToGround: false,
- materialType: mars3d.MaterialType.PolylineDash,
- materialOptions: {
- color: '#ff0000',
- dashLength: 16.0
- }
- }
- })
- graphicLayer.addGraphic(data.graphic)
- }
- }
- UavInspection.prototype.updateDashLine = function (lng, lat, height) {
- let id = `dashline-${this.id}`;
- if (this.graphicMap.has(id)) {
- let data = this.graphicMap.get(id);
- (async () => {
- let h = await getHeightByLngLat(getMap(), lng, lat);
- if (data.graphic) {
- data.graphic.setCallbackPositions([[lng, lat, height], [lng, lat, h]]);
- }
- })();
- }
- }
- /**
- * 计算正方形在三维空间中的四个顶点坐标
- */
- UavInspection.prototype.calculateSquareVertices = function (lng, lat, angle, step) {
- let centerPoint = Cesium.Cartesian3.fromDegrees(lng, lat, 0);
- // 获取中心点处的ENU坐标系基向量(东、北、天)
- const matrix = Cesium.Transforms.eastNorthUpToFixedFrame(centerPoint);
- const east = new Cesium.Cartesian3();
- const north = new Cesium.Cartesian3();
- Cesium.Matrix4.getColumn(matrix, 0, east); // 东方向单位向量
- Cesium.Matrix4.getColumn(matrix, 1, north); // 北方向单位向量
- const halfLen = step / 2;
- const angleRad = Cesium.Math.toRadians(angle); // 转为弧度
- // 1. 计算目标方向向量(θ方向)在ENU坐标系的投影分量
- const cosTheta = Math.cos(angleRad);
- const sinTheta = Math.sin(angleRad);
- // 计算主方向单位向量 (基于东和北分量)
- const dirX = cosTheta;
- const dirY = -sinTheta; // 负号用于处理顺时针旋转
- // 构造三维空间中的方向向量
- const directionVector = new Cesium.Cartesian3();
- Cesium.Cartesian3.multiplyByScalar(east, dirX, directionVector);
- Cesium.Cartesian3.add(
- directionVector,
- Cesium.Cartesian3.multiplyByScalar(north, dirY, new Cesium.Cartesian3()),
- directionVector
- );
- // 2. 计算垂直方向向量 (θ+90°) 的分量
- const perpX = -sinTheta;
- const perpY = -cosTheta;
- // 构造三维空间中的垂直向量
- const perpendicularVector = new Cesium.Cartesian3();
- Cesium.Cartesian3.multiplyByScalar(east, perpX, perpendicularVector);
- Cesium.Cartesian3.add(
- perpendicularVector,
- Cesium.Cartesian3.multiplyByScalar(north, perpY, new Cesium.Cartesian3()),
- perpendicularVector
- );
- // 3. 计算四个顶点
- const result = [];
- const scratch = new Cesium.Cartesian3();
- // 顶点计算函数 (避免重复代码)
- const calculatePoint = (xFactor, yFactor) => {
- const point = scratch;
- // point = centerPoint + (directionVector * xFactor * halfLen) + (perpendicularVector * yFactor * halfLen)
- Cesium.Cartesian3.multiplyByScalar(directionVector, xFactor * halfLen, point);
- Cesium.Cartesian3.add(
- point,
- Cesium.Cartesian3.multiplyByScalar(perpendicularVector, yFactor * halfLen, new Cesium.Cartesian3()),
- point
- );
- return Cesium.Cartesian3.add(centerPoint, point, new Cesium.Cartesian3());
- };
- // 生成4个顶点(顺时针顺序)
- result.push(calculatePoint(1, -1)); // 起点:右下
- result.push(calculatePoint(1, 1)); // 右上
- result.push(calculatePoint(-1, 1)); // 左上
- result.push(calculatePoint(-1, -1)); // 左下
- return result;
- }
- UavInspection.prototype.getSquareVertices = function (pos, pos1, pos2, step) {
- let angle = this.calculateBearingFromEast(pos1, pos2);
- let r = this.calculateSquareVertices(pos[0], pos[1], angle, step);
- let result = { center: pos, positions: [] };
- for (let i = 0; i < r.length; i++) {
- const cartographic = Cesium.Cartographic.fromCartesian(r[i]);
- let position = [Cesium.Math.toDegrees(cartographic.longitude), Cesium.Math.toDegrees(cartographic.latitude), cartographic.height];
- result.positions.push(position);
- }
- return result;
- }
- UavInspection.prototype.getRectangleVertices = function (pos1, pos2, step) {
- let mid1 = Cesium.Cartesian3.fromDegrees(pos1[0], pos1[1], 0);
- let mid2 = Cesium.Cartesian3.fromDegrees(pos2[0], pos2[1], 0);
- const { Cartesian3, Ellipsoid, Cartographic } = Cesium;
- const ellipsoid = Ellipsoid.WGS84;
- // 计算中点连线方向向量
- const v1 = Cartesian3.subtract(mid2, mid1, new Cartesian3());
- const distance = Cartesian3.magnitude(v1);
- Cartesian3.normalize(v1, v1);
- // 计算垂直方向向量
- const center = Cartesian3.midpoint(mid1, mid2, new Cartesian3());
- const normal = ellipsoid.geodeticSurfaceNormal(center, new Cartesian3());
- const v2 = Cartesian3.cross(normal, v1, new Cartesian3());
- Cartesian3.normalize(v2, v2);
- // 缩放垂直向量到指定长度
- const halfWidth = Cartesian3.multiplyByScalar(v2, step / 2.0, new Cartesian3());
- // 计算四个顶点的笛卡尔坐标
- const cornersCartesian = [
- Cartesian3.add(mid1, Cartesian3.negate(halfWidth, new Cartesian3()), new Cartesian3()), // SW
- Cartesian3.add(mid1, halfWidth, new Cartesian3()), // NW
- Cartesian3.add(mid2, halfWidth, new Cartesian3()), // NE
- Cartesian3.add(mid2, Cartesian3.negate(halfWidth, new Cartesian3()), new Cartesian3()) // SE
- ];
- // 转换为经纬度(弧度)坐标
- const cornersRadians = cornersCartesian.map(cartesian => {
- return Cartographic.fromCartesian(cartesian);
- });
- // 转换为度数
- return cornersRadians.map(rad => {
- return [Cesium.Math.toDegrees(rad.longitude), Cesium.Math.toDegrees(rad.latitude), rad.height];
- });
- }
- UavInspection.prototype.divideSegment = function (pos1, pos2, step) {
- let p1 = Cesium.Cartesian3.fromDegrees(pos1[0], pos1[1], 0);
- let p2 = Cesium.Cartesian3.fromDegrees(pos2[0], pos2[1], 0);
- let result = [];
- let lastPos = pos1;
- let currentPos;
- let distance = Cesium.Cartesian3.distance(p1, p2);
- let d = step;
- while (d < distance - 0.1) {
- let t = d / distance;
- const interpolatedCartesian = new Cesium.Cartesian3();
- Cesium.Cartesian3.lerp(p1, p2, t, interpolatedCartesian);
- const cartographic = Cesium.Cartographic.fromCartesian(interpolatedCartesian);
- currentPos = [Cesium.Math.toDegrees(cartographic.longitude), Cesium.Math.toDegrees(cartographic.latitude), cartographic.height];
- result.push({
- pos1: lastPos,
- pos2: currentPos,
- positions: this.getRectangleVertices(lastPos, currentPos, step)
- });
- lastPos = currentPos;
- d += step;
- }
- result.push({
- pos1: currentPos,
- pos2: pos2,
- positions: this.getRectangleVertices(currentPos, pos2, step)
- });
- return result;
- }
- UavInspection.prototype.divideLine = function (positions, step) {
- let result = [];
- for (let i = 0; i < positions.length - 1; i++) {
- let pos1 = positions[i];
- let pos2 = positions[i + 1];
- let array = this.divideSegment(pos1, pos2, step);
- result.push(...array);
- }
- return result.slice(0, -1);
- }
- UavInspection.prototype.drawBlocks = function (lng, lat) {
- if (this.segmentPositions && this.segmentPositions.length > 0) {
- let index = -1;
- for (let [i, pos] of this.segmentPositions.entries()) {
- let distance1 = Cesium.Cartesian3.distance(Cesium.Cartesian3.fromDegrees(pos.pos1[0], pos.pos1[1], 0), Cesium.Cartesian3.fromDegrees(pos.pos2[0], pos.pos2[1], 0));
- let distance2 = Cesium.Cartesian3.distance(Cesium.Cartesian3.fromDegrees(lng, lat, 0), Cesium.Cartesian3.fromDegrees(pos.pos1[0], pos.pos1[1], 0));
- let distance3 = Cesium.Cartesian3.distance(Cesium.Cartesian3.fromDegrees(lng, lat, 0), Cesium.Cartesian3.fromDegrees(pos.pos2[0], pos.pos2[1], 0));
- if (distance1 - (distance2 + distance3) > -0.1) {
- index = i;
- }
- }
- for (let i = 0; i <= index; i++) {
- let pos = this.segmentPositions[i];
- let pos2
- if (i + 1 < this.segmentPositions.length) {
- pos2 = this.segmentPositions[i + 1];
- } else {
- pos2 = this.segmentPositions[i - 1];
- }
- this.drawBlock(i, pos);
- }
- } else {
- if (!this.lastBlockPos) {
- this.lastBlockPos = [lng, lat, 0];
- this.blockIndex = 0;
- } else {
- let distance = Cesium.Cartesian3.distance(Cesium.Cartesian3.fromDegrees(this.lastBlockPos[0], this.lastBlockPos[1], 0), Cesium.Cartesian3.fromDegrees(lng, lat, 0));
- if (distance >= this.step) {
- let pos2 = [lng, lat, 0];
- let vertices = this.getRectangleVertices(this.lastBlockPos, pos2, this.step);
- this.drawBlock(this.blockIndex++, { positions: vertices });
- this.lastBlockPos = [lng, lat, 0];
- }
- }
- }
- }
- UavInspection.prototype.drawBlock = function (i, pos) {
- let id = `block-${i}`;
- let data;
- if (this.graphicMap.has(id)) {
- return;
- } else {
- data = { id: id, type: 'block' };
- this.graphicMap.set(id, data);
- }
- if (!data.graphic) {
- data.graphic = new mars3d.graphic.PolygonPrimitive({
- positions: pos.positions,
- style: {
- color: "#ff0000",
- opacity: 0.3,
- clampToGround: true
- }
- })
- graphicLayer.addGraphic(data.graphic)
- }
- }
- /**
- * 计算两点间方向(正东为0°,顺时针到360°)
- * @returns {number} 方向角度(0°到360°,正东方向为0°)
- */
- UavInspection.prototype.calculateBearingFromEast = function (pos1, pos2) {
- const point1 = Cesium.Cartographic.fromDegrees(pos1[0], pos1[1]);
- const point2 = Cesium.Cartographic.fromDegrees(pos2[0], pos2[1]);
- const lon1 = point1.longitude;
- const lat1 = point1.latitude;
- const lon2 = point2.longitude;
- const lat2 = point2.latitude;
- // 经度差(考虑跨日期变更线)
- const dLon = lon2 - lon1;
- // 计算Y分量(与正北相关)
- const y = Math.sin(dLon) * Math.cos(lat2);
- // 计算X分量(与正北相关)
- const x = Math.cos(lat1) * Math.sin(lat2) -
- Math.sin(lat1) * Math.cos(lat2) * Math.cos(dLon);
- // 计算初始方位角(0°为正北,顺时针)
- const bearingRad = Math.atan2(y, x);
- let bearingDeg = Cesium.Math.toDegrees(bearingRad);
- // 转换为0°~360°范围
- if (bearingDeg < 0) {
- bearingDeg += 360;
- }
- // 坐标系转换:正北0° → 正东0°
- // 转换公式: (bearingDeg + 90) % 360
- return (bearingDeg + 90) % 360;
- }
- return UavInspection;
- })()
- function createUavInspection(id, positions) {
- if (!uavInspectionMap.has(id)) {
- let uavInspection = new UavInspection(id, positions);
- uavInspection.createDrone();
- uavInspection.addLabels();
- uavInspectionMap.set(id, uavInspection);
- }
- }
- function uavInspectionExists(id) {
- return uavInspectionMap.has(id);
- }
- function updateUavInspectionPosition(id, lng, lat, height) {
- if (uavInspectionMap.has(id)) {
- let uavInspection = uavInspectionMap.get(id);
- uavInspection.updateDronePosition(lng, lat, height);
- }
- }
- function clearUavInspections() {
- for (let id of uavInspectionMap.keys()) {
- let uavInspection = uavInspectionMap.get(id);
- uavInspectionMap.delete(id);
- uavInspection.clear();
- }
- graphicLayer = undefined;
- }
- // 定时清理
- setInterval(() => {
- for (let id of uavInspectionMap.keys()) {
- let uavInspection = uavInspectionMap.get(id);
- if (new Date().getTime() - uavInspection.lastTime > 20 * 1000) {
- uavInspectionMap.delete(id);
- uavInspection.clear();
- }
- }
- }, 1000);
- // 无人机经过点位回调
- function registerPointCallback(callback) {
- pointCallback = callback;
- }
- export { uavInspectionExists, createUavInspection, updateUavInspectionPosition, clearUavInspections, registerPointCallback }
复制代码 实时接收WebSocket数据,展示无人机、航线、地面色块
部分代码如下- import { uavInspectionExists, createUavInspection, updateUavInspectionPosition } from '@/components/mars3dMap/js/uavInspection.js'
- async function processWebSocketData(json) {
- if (json.method && json.method == "FLIGHT" && json.data) {
- let data = json.data;
- let id = data.droneSN;
- let model = data.model; // 飞机型号
- let lng = data.longitude;
- let lat = data.latitude;
- let alt = data.altitude; // 海拔高度
- let heading = data.heading; // 朝向(度)
- let pitch = data.pitch; // 俯仰角(度)
- let roll = data.roll; // 翻转角(度)
- let groundSpeed = data.groundSpeed; // 地面速度,单位m/s
- if (!uavInspectionExists(id)) {
- let positions;
- if (id == config.droneSN) {
- positions = config.passingPoints;
- } else {
- positions = [];
- }
- createUavInspection(id, positions)
- }
- updateUavInspectionPosition(id, lng, lat, alt);
- }
- }
复制代码 效果截图
截图说明:由于测试笔记本比较卡,所以红虚线、红色块和无人机的位置不同步
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |