|
|
@@ -106,6 +106,141 @@ import ModelUnpack from "@/components/modelUnpack.vue";
|
|
|
import { WindLayer } from "cesium-wind";
|
|
|
import windGridData from "./windGridData.json";
|
|
|
import { HeightReference } from "cesium";
|
|
|
+
|
|
|
+class AdvancedBillboardGenerator {
|
|
|
+ constructor() {
|
|
|
+ this.cache = new Map();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 生成高级灯牌
|
|
|
+ generateAdvancedBillboard(data) {
|
|
|
+ const {
|
|
|
+ title,
|
|
|
+ statusItems = [],
|
|
|
+ width = 120,
|
|
|
+ backgroundColor = "rgba(13, 38, 77, 0.95)",
|
|
|
+ borderGradient = ["#00e5ff", "#0077ff"],
|
|
|
+ titleColor = "#4fc3f7",
|
|
|
+ valueColor = "#00ff88",
|
|
|
+ labelColor = "#b3e5fc",
|
|
|
+ } = data;
|
|
|
+
|
|
|
+ const cacheKey = JSON.stringify(data);
|
|
|
+ if (this.cache.has(cacheKey)) {
|
|
|
+ return this.cache.get(cacheKey);
|
|
|
+ }
|
|
|
+
|
|
|
+ const canvas = document.createElement("canvas");
|
|
|
+ const ctx = canvas.getContext("2d");
|
|
|
+ canvas.width = width;
|
|
|
+
|
|
|
+ // 计算高度
|
|
|
+ const titleHeight = 30;
|
|
|
+ const itemHeight = 25;
|
|
|
+ const padding = 8;
|
|
|
+ const height = titleHeight + statusItems.length * itemHeight + padding * 2;
|
|
|
+ canvas.height = height;
|
|
|
+
|
|
|
+ // 绘制背景
|
|
|
+ this.drawAdvancedBackground(
|
|
|
+ ctx,
|
|
|
+ width,
|
|
|
+ height,
|
|
|
+ backgroundColor,
|
|
|
+ borderGradient
|
|
|
+ );
|
|
|
+
|
|
|
+ // 绘制标题
|
|
|
+ ctx.fillStyle = titleColor;
|
|
|
+ ctx.font = "bold 14px sans-serif";
|
|
|
+ ctx.textAlign = "center";
|
|
|
+ ctx.fillText(title, width / 2, padding + 14);
|
|
|
+
|
|
|
+ // 绘制状态项
|
|
|
+ statusItems.forEach((item, index) => {
|
|
|
+ const y = titleHeight + padding + index * itemHeight;
|
|
|
+
|
|
|
+ // 标签
|
|
|
+ ctx.fillStyle = labelColor;
|
|
|
+ ctx.font = "12px sans-serif";
|
|
|
+ ctx.textAlign = "left";
|
|
|
+ ctx.fillText(item.label + ":", padding, y + 12);
|
|
|
+
|
|
|
+ // 值
|
|
|
+ ctx.fillStyle = valueColor;
|
|
|
+ ctx.font = "bold 12px sans-serif";
|
|
|
+ ctx.textAlign = "right";
|
|
|
+ ctx.fillText(item.value, width - padding, y + 12);
|
|
|
+ });
|
|
|
+
|
|
|
+ const dataURL = canvas.toDataURL();
|
|
|
+ this.cache.set(cacheKey, dataURL);
|
|
|
+
|
|
|
+ return dataURL;
|
|
|
+ }
|
|
|
+
|
|
|
+ drawAdvancedBackground(ctx, width, height, bgColor, borderGradient) {
|
|
|
+ const borderRadius = 12;
|
|
|
+
|
|
|
+ // 主背景
|
|
|
+ this.drawRoundedRect(ctx, 0, 0, width, height, borderRadius, bgColor);
|
|
|
+
|
|
|
+ // 边框渐变
|
|
|
+ const gradient = ctx.createLinearGradient(0, 0, width, height);
|
|
|
+ gradient.addColorStop(0, borderGradient[0]);
|
|
|
+ gradient.addColorStop(1, borderGradient[1]);
|
|
|
+
|
|
|
+ this.drawRoundedRect(
|
|
|
+ ctx,
|
|
|
+ 2,
|
|
|
+ 2,
|
|
|
+ width - 4,
|
|
|
+ height - 4,
|
|
|
+ borderRadius - 2,
|
|
|
+ gradient,
|
|
|
+ 2
|
|
|
+ );
|
|
|
+
|
|
|
+ // 内发光效果
|
|
|
+ ctx.shadowColor = borderGradient[0];
|
|
|
+ ctx.shadowBlur = 10;
|
|
|
+ this.drawRoundedRect(
|
|
|
+ ctx,
|
|
|
+ 4,
|
|
|
+ 4,
|
|
|
+ width - 8,
|
|
|
+ height - 8,
|
|
|
+ borderRadius - 4,
|
|
|
+ "transparent",
|
|
|
+ 1
|
|
|
+ );
|
|
|
+ ctx.shadowBlur = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ drawRoundedRect(ctx, x, y, width, height, radius, color, lineWidth = 0) {
|
|
|
+ ctx.beginPath();
|
|
|
+ ctx.moveTo(x + radius, y);
|
|
|
+ ctx.lineTo(x + width - radius, y);
|
|
|
+ ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
|
|
|
+ ctx.lineTo(x + width, y + height - radius);
|
|
|
+ ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
|
|
|
+ ctx.lineTo(x + radius, y + height);
|
|
|
+ ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
|
|
|
+ ctx.lineTo(x, y + radius);
|
|
|
+ ctx.quadraticCurveTo(x, y, x + radius, y);
|
|
|
+ ctx.closePath();
|
|
|
+
|
|
|
+ if (lineWidth > 0) {
|
|
|
+ ctx.strokeStyle = color;
|
|
|
+ ctx.lineWidth = lineWidth;
|
|
|
+ ctx.stroke();
|
|
|
+ } else {
|
|
|
+ ctx.fillStyle = color;
|
|
|
+ ctx.fill();
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
export default {
|
|
|
name: "windMap3D",
|
|
|
components: {
|
|
|
@@ -150,7 +285,7 @@ export default {
|
|
|
verticalExaggeration: 1.0,
|
|
|
dixingAdd: false,
|
|
|
infoBoxes: [],
|
|
|
- entityxy: {}
|
|
|
+ entityxy: {},
|
|
|
};
|
|
|
},
|
|
|
mounted() {
|
|
|
@@ -401,11 +536,11 @@ export default {
|
|
|
},
|
|
|
// 展示所选风场的风机
|
|
|
showWindFromStation(viewer) {
|
|
|
- this.infoBoxes = [];
|
|
|
- fjLonLatJson.data.forEach((e, index) => {
|
|
|
- this.showStatuswind(viewer, e);
|
|
|
- });
|
|
|
- this.resetWindViewport();
|
|
|
+ this.infoBoxes = [];
|
|
|
+ fjLonLatJson.data.forEach((e, index) => {
|
|
|
+ this.showStatuswind(viewer, e);
|
|
|
+ });
|
|
|
+ this.resetWindViewport();
|
|
|
},
|
|
|
// 根据状态展示不同颜色风机贴图
|
|
|
showStatuswind(viewer, e) {
|
|
|
@@ -460,17 +595,11 @@ export default {
|
|
|
hpRoll
|
|
|
);
|
|
|
|
|
|
- // const statueColor = this.getWtStatue(wtStatue);
|
|
|
- const statueFJ = this.getWtStatue(status, 'en');
|
|
|
- const statueZh = this.getWtStatue(status, 'zh');
|
|
|
+ // const statueColor = this.getWtStatue(wtStatue);
|
|
|
+ const statueFJ = this.getWtStatue(status, "en");
|
|
|
+ const statueZh = this.getWtStatue(status, "zh");
|
|
|
|
|
|
- // 获取这些位置的精确地形高度
|
|
|
- Cesium.sampleTerrainMostDetailed(this.viewer.terrainProvider, position)
|
|
|
- .then((pos) => {})
|
|
|
- .catch(function (error) {
|
|
|
- console.log("获取地形高度时发生错误:", error);
|
|
|
- });
|
|
|
- let that = this
|
|
|
+ let that = this;
|
|
|
const entity = viewer.entities.add({
|
|
|
name, // 模型名称
|
|
|
position, // 模型位置
|
|
|
@@ -478,73 +607,111 @@ export default {
|
|
|
animation: false,
|
|
|
model: {
|
|
|
uri: this.dixingAdd
|
|
|
- // ? "/public/static/model/fjSolo/model.glb"
|
|
|
- // ? "/public/static/model/fjStatus/fj_anmation_bw.glb"
|
|
|
- ? `/public/static/model/fjStatus/fj_${statueFJ}.glb`
|
|
|
- : "/public/static/model/dixing/model.glb",
|
|
|
- // : "/public/static/model/dixing/terrain.glb",
|
|
|
+ ? // ? "/public/static/model/fjSolo/model.glb"
|
|
|
+ // ? "/public/static/model/fjStatus/fj_anmation_bw.glb"
|
|
|
+ `/public/static/model/fjStatus/fj_${statueFJ}.glb`
|
|
|
+ : "/public/static/model/dixing/model.glb",
|
|
|
+ // : "/public/static/model/dixing/terrain.glb",
|
|
|
scale: this.dixingAdd ? 20 : 10000,
|
|
|
// 模型贴地
|
|
|
- // heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
|
|
|
- // heightReference: Cesium.HeightReference.NONE,
|
|
|
- // silhouetteSize: this.dixingAdd ? 2 : 0,
|
|
|
- // silhouetteColor: Cesium.Color.fromCssColorString(statueColor),
|
|
|
- // runAnimations: wtStatue !== 7 ? false : true,
|
|
|
+ // heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
|
|
|
+ // heightReference: Cesium.HeightReference.NONE,
|
|
|
+ // silhouetteSize: this.dixingAdd ? 2 : 0,
|
|
|
+ // silhouetteColor: Cesium.Color.fromCssColorString(statueColor),
|
|
|
+ // runAnimations: wtStatue !== 7 ? false : true,
|
|
|
},
|
|
|
});
|
|
|
+
|
|
|
+ // 使用高级灯牌生成器
|
|
|
+ const advancedGenerator = new AdvancedBillboardGenerator();
|
|
|
+
|
|
|
+ let statusItems = [];
|
|
|
+ const statusArray = [
|
|
|
+ { label: "风速", value: "10m/s" },
|
|
|
+ { label: "状态", value: statueZh },
|
|
|
+ { label: "转速", value: "1000p" },
|
|
|
+ { label: "功率", value: "50kW" },
|
|
|
+ { label: "其他参数A", value: "...." },
|
|
|
+ { label: "其他参数B", value: "...." },
|
|
|
+ { label: "其他参数C", value: "...." },
|
|
|
+ { label: "其他参数D", value: "...." },
|
|
|
+ { label: "其他参数E", value: "...." },
|
|
|
+ { label: "其他参数F", value: "...." },
|
|
|
+ ];
|
|
|
+
|
|
|
+ for (let i = 0; i <= this.randomNum(1, 9); i++) {
|
|
|
+ statusItems.push(statusArray[i]);
|
|
|
+ }
|
|
|
+
|
|
|
+ const billboardImage = advancedGenerator.generateAdvancedBillboard({
|
|
|
+ title: name,
|
|
|
+ statusItems,
|
|
|
+ borderGradient: ["#00e5ff", "#2979ff"],
|
|
|
+ });
|
|
|
+
|
|
|
const entityxy = viewer.entities.add({
|
|
|
- name, // 模型名称
|
|
|
- position, // 模型位置
|
|
|
- orientation, // 模型朝向
|
|
|
- //添加标签
|
|
|
- label: {
|
|
|
- text: `${name}\n\n风速: 10m/s\n\n状态: ${statueZh}`,
|
|
|
- font: "14px sans-serif",
|
|
|
- fillColor: Cesium.Color.fromBytes(255, 255, 255),
|
|
|
- pixelOffset: new Cesium.Cartesian2(-50, -20)
|
|
|
- },
|
|
|
+ // name, // 模型名称
|
|
|
+ // position, // 模型位置
|
|
|
+ // orientation, // 模型朝向
|
|
|
+ // //添加标签
|
|
|
+ // label: {
|
|
|
+ // text: `${name}\n\n风速: 10m/s\n\n状态: ${statueZh}`,
|
|
|
+ // font: "14px sans-serif",
|
|
|
+ // fillColor: Cesium.Color.fromBytes(255, 255, 255),
|
|
|
+ // pixelOffset: new Cesium.Cartesian2(-50, -20),
|
|
|
+ // },
|
|
|
+ // billboard: {
|
|
|
+ // image: backPng,
|
|
|
+ // scale: 0.2,
|
|
|
+ // verticalOrigin: Cesium.VerticalOrigin.CENTER,
|
|
|
+ // pixelOffset: new Cesium.Cartesian2(-50, -20),
|
|
|
+ // // heightReference: Cesium.HeightReference.CLAMP_TO_GROUND
|
|
|
+ // },
|
|
|
+ name,
|
|
|
+ position,
|
|
|
billboard: {
|
|
|
- image: backPng,
|
|
|
- scale: 0.2,
|
|
|
- verticalOrigin: Cesium.VerticalOrigin.CENTER,
|
|
|
- pixelOffset: new Cesium.Cartesian2(-50, -20)
|
|
|
- // heightReference: Cesium.HeightReference.CLAMP_TO_GROUND
|
|
|
+ image: billboardImage,
|
|
|
+ scale: 1,
|
|
|
+ verticalOrigin: Cesium.VerticalOrigin.BOTTOM, // 原来已经是CENTER,现在改为BOTTOM
|
|
|
+ pixelOffset: new Cesium.Cartesian2(80, -50), // 原来是-20,现在改为30,向上移动
|
|
|
+ eyeOffset: new Cesium.Cartesian3(0, 0, 0), // 保持固定大小
|
|
|
+ heightReference: Cesium.HeightReference.NONE,
|
|
|
},
|
|
|
});
|
|
|
|
|
|
this.dixingAdd = true;
|
|
|
|
|
|
- viewer.camera.moveEnd.addEventListener(() => {
|
|
|
- // 获取相机的笛卡尔3D坐标
|
|
|
- const position = viewer.camera.position;
|
|
|
-
|
|
|
- // 将笛卡尔坐标转换为地理坐标(弧度)
|
|
|
- const cartographic = Cesium.Cartographic.fromCartesian(position);
|
|
|
-
|
|
|
- // 高度(以米为单位)
|
|
|
- // 注意:这个高度是相对于WGS84椭球体的高度,不是海拔高度(MSL)
|
|
|
- const height = Cesium.Math.toDegrees(cartographic.height);
|
|
|
- if (height > 3000000) {
|
|
|
- entityxy.show = false
|
|
|
- console.log("entityxy false", entityxy.show);
|
|
|
- }
|
|
|
- if (height < 3000000) {
|
|
|
- entityxy.show = true
|
|
|
- console.log("entityxy true", entityxy.show);
|
|
|
- }
|
|
|
- // 强制请求一次场景重绘
|
|
|
- if (viewer && viewer.scene) {
|
|
|
- viewer.scene.requestRender();
|
|
|
- }
|
|
|
- console.log('相机高度 (椭球体高):', height, '米');
|
|
|
- });
|
|
|
+ viewer.camera.moveEnd.addEventListener(() => {
|
|
|
+ // 获取相机的笛卡尔3D坐标
|
|
|
+ const position = viewer.camera.position;
|
|
|
+
|
|
|
+ // 将笛卡尔坐标转换为地理坐标(弧度)
|
|
|
+ const cartographic = Cesium.Cartographic.fromCartesian(position);
|
|
|
+
|
|
|
+ // 高度(以米为单位)
|
|
|
+ // 注意:这个高度是相对于WGS84椭球体的高度,不是海拔高度(MSL)
|
|
|
+ const height = Cesium.Math.toDegrees(cartographic.height);
|
|
|
+ if (height > 3000000) {
|
|
|
+ entityxy.show = false;
|
|
|
+ console.log("entityxy false", entityxy.show);
|
|
|
+ }
|
|
|
+ if (height < 3000000) {
|
|
|
+ entityxy.show = true;
|
|
|
+ console.log("entityxy true", entityxy.show);
|
|
|
+ }
|
|
|
+ // 强制请求一次场景重绘
|
|
|
+ if (viewer && viewer.scene) {
|
|
|
+ viewer.scene.requestRender();
|
|
|
+ }
|
|
|
+ console.log("相机高度 (椭球体高):", height, "米");
|
|
|
+ });
|
|
|
|
|
|
// 创建事件处理器
|
|
|
const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
|
|
|
handler.setInputAction(function (movement) {
|
|
|
var position = movement.position;
|
|
|
var pickedObject = viewer.scene.pick(position);
|
|
|
- if (pickedObject && pickedObject.id === entity) {
|
|
|
+ if (pickedObject && pickedObject.id === entity) {
|
|
|
console.log("你点击了标签或模型!", entity);
|
|
|
// console.log("标签或模型数据!", val);
|
|
|
that.modelVal = model;
|
|
|
@@ -727,6 +894,18 @@ export default {
|
|
|
this.showcomModelDia = val;
|
|
|
this.modelVal = null;
|
|
|
},
|
|
|
+
|
|
|
+ randomNum(minNum, maxNum) {
|
|
|
+ switch (arguments.length) {
|
|
|
+ case 1:
|
|
|
+ return parseInt(Math.random() * minNum + 1, 10);
|
|
|
+ case 2:
|
|
|
+ return parseInt(Math.random() * (maxNum - minNum + 1) + minNum, 10);
|
|
|
+ default:
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
csceneElliposid(viewer) {
|
|
|
let that = this;
|
|
|
// 获取 scene 和 ellipsoid
|