|
|
@@ -2,27 +2,59 @@
|
|
|
<div class="mapBox">
|
|
|
<div id="cesiumContainer" style="width: 100%; height: 100vh"></div>
|
|
|
<div class="menuBox" :class="allyShow ? '' : 'switch'">
|
|
|
- <el-button type="primary" @click="resetViewport()">初始化视角</el-button>
|
|
|
- <el-button
|
|
|
- :type="windLayer ? 'danger' : 'primary'"
|
|
|
- @click="switchWindLayer"
|
|
|
- >{{ windLayer ? "关闭" : "显示" }}风场图</el-button
|
|
|
- >
|
|
|
- <el-button
|
|
|
- :type="cloudLayer ? 'danger' : 'primary'"
|
|
|
- @click="switchCloudLayer"
|
|
|
- >{{ cloudLayer ? "关闭" : "显示" }}云图</el-button
|
|
|
- >
|
|
|
- <el-button
|
|
|
- :type="rainLayer ? 'danger' : 'primary'"
|
|
|
- @click="switchRainLayer"
|
|
|
- >{{ rainLayer ? "关闭" : "显示" }}降雨图</el-button
|
|
|
- >
|
|
|
- <el-button
|
|
|
- :type="rainLayer ? 'danger' : 'primary'"
|
|
|
- @click="switchTemperatureLayerr"
|
|
|
- >{{ rainLayer ? "关闭" : "显示" }}温度图</el-button
|
|
|
- >
|
|
|
+ <div class="item">
|
|
|
+ <span>图源:</span>
|
|
|
+ <el-select
|
|
|
+ v-model="basicMapId"
|
|
|
+ size="small"
|
|
|
+ style="width: 120px"
|
|
|
+ @change="setMapImageryProvider"
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="item in basicMapList"
|
|
|
+ :key="item.id"
|
|
|
+ :label="item.name"
|
|
|
+ :value="item.id"
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
+ </div>
|
|
|
+ <div class="item">
|
|
|
+ <el-button type="primary" size="small" @click="resetViewport()"
|
|
|
+ >初始化视角</el-button
|
|
|
+ >
|
|
|
+ </div>
|
|
|
+ <div class="item">
|
|
|
+ <el-button
|
|
|
+ size="small"
|
|
|
+ :type="windLayer ? 'danger' : 'primary'"
|
|
|
+ @click="switchWindLayer"
|
|
|
+ >{{ windLayer ? "关闭" : "显示" }}风场图</el-button
|
|
|
+ >
|
|
|
+ </div>
|
|
|
+ <div class="item">
|
|
|
+ <el-button
|
|
|
+ size="small"
|
|
|
+ :type="cloudLayer ? 'danger' : 'primary'"
|
|
|
+ @click="switchCloudLayer"
|
|
|
+ >{{ cloudLayer ? "关闭" : "显示" }}云图</el-button
|
|
|
+ >
|
|
|
+ </div>
|
|
|
+ <div class="item">
|
|
|
+ <el-button
|
|
|
+ size="small"
|
|
|
+ :type="rainLayer ? 'danger' : 'primary'"
|
|
|
+ @click="switchRainLayer"
|
|
|
+ >{{ rainLayer ? "关闭" : "显示" }}降雨图</el-button
|
|
|
+ >
|
|
|
+ </div>
|
|
|
+ <div class="item">
|
|
|
+ <el-button
|
|
|
+ size="small"
|
|
|
+ :type="rainLayer ? 'danger' : 'primary'"
|
|
|
+ @click="switchTemperatureLayerr"
|
|
|
+ >{{ rainLayer ? "关闭" : "显示" }}温度图</el-button
|
|
|
+ >
|
|
|
+ </div>
|
|
|
<el-tooltip
|
|
|
class="box-item"
|
|
|
effect="dark"
|
|
|
@@ -43,14 +75,52 @@
|
|
|
</el-icon>
|
|
|
</el-tooltip>
|
|
|
</div>
|
|
|
+ <div
|
|
|
+ class="tag"
|
|
|
+ :style="`left:${userClickLeft}px;top:${userClickTop}px`"
|
|
|
+ v-if="tagMsg || tagMsg === ''"
|
|
|
+ >
|
|
|
+ <el-icon class="is-loading" v-if="tagMsg === ''">
|
|
|
+ <Loading />
|
|
|
+ </el-icon>
|
|
|
+ <span v-else>{{ tagMsg || "" }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="devInfoBox" v-if="showDevInfoBox">
|
|
|
+ <div class="item">=== 帧率与内存 ===</div>
|
|
|
+ <div class="item">运行帧率: {{ fps }}</div>
|
|
|
+ <div class="item">响应时长: {{ ms }}</div>
|
|
|
+ <div class="item">内存占用: {{ jsHeapSize }}</div>
|
|
|
+ <template v-if="gVendor || gRenderer">
|
|
|
+ <div class="item" style="margin-top: 12px">
|
|
|
+ ==== 显卡信息 ====
|
|
|
+ </div>
|
|
|
+ <el-tooltip
|
|
|
+ effect="dark"
|
|
|
+ :content="gVendor"
|
|
|
+ placement="top-end"
|
|
|
+ v-if="gVendor"
|
|
|
+ >
|
|
|
+ <div class="item">制造商: {{ gVendor }}</div>
|
|
|
+ </el-tooltip>
|
|
|
+ <el-tooltip
|
|
|
+ effect="dark"
|
|
|
+ :content="gRenderer"
|
|
|
+ placement="top-end"
|
|
|
+ v-if="gRenderer"
|
|
|
+ >
|
|
|
+ <div class="item">型号: {{ gRenderer }}</div>
|
|
|
+ </el-tooltip>
|
|
|
+ </template>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script>
|
|
|
-import * as Cesium from "../Cesium";
|
|
|
-import "../Cesium/Widgets/widgets.css";
|
|
|
+// import * as Cesium from "../Cesium";
|
|
|
+// import "../Cesium/Widgets/widgets.css";
|
|
|
+import * as Cesium from "cesium";
|
|
|
+import "cesium/Build/Cesium/Widgets/widgets.css";
|
|
|
import Windy from "../assets/wind/Windy_source.js";
|
|
|
-import SingleImageProvider from "../assets/wind/SingleImageProvider";
|
|
|
|
|
|
import basicGeoJson from "../assets/geoJson/basic.json";
|
|
|
import windLineJson from "../assets/geoJson/windLine_2017121300.json";
|
|
|
@@ -58,19 +128,62 @@ import windLineJson from "../assets/geoJson/windLine_2017121300.json";
|
|
|
import axios from "axios";
|
|
|
export default {
|
|
|
name: "CesiumMap",
|
|
|
+
|
|
|
data() {
|
|
|
return {
|
|
|
+ checkMode: false, // 调试模式
|
|
|
allyShow: false,
|
|
|
viewer: null,
|
|
|
windLayer: null, // 风场图
|
|
|
windLayerTimmer: null, // 风场图计时器
|
|
|
cloudLayer: null, // 卫星云图
|
|
|
- rainLayer: null, //
|
|
|
+ rainLayer: null, // 降雨图
|
|
|
+ basicMapId: "gaodeyingxiang", // 地球底图 ID
|
|
|
+ // 地球底图数组
|
|
|
+ basicMapList: [
|
|
|
+ {
|
|
|
+ id: "gaodeyingxiang",
|
|
|
+ name: "高德影像地图",
|
|
|
+ url: "https://webst02.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}",
|
|
|
+ minimumLevel: 3,
|
|
|
+ maximumLevel: 18,
|
|
|
+ credit: "basicMap",
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: "gaodeshiliang",
|
|
|
+ name: "高德矢量地图",
|
|
|
+ url: "https://webrd01.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}",
|
|
|
+ minimumLevel: 3,
|
|
|
+ maximumLevel: 18,
|
|
|
+ credit: "basicMap",
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: "carto",
|
|
|
+ name: "Carto地图",
|
|
|
+ url: "http://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png",
|
|
|
+ credit: "basicMap",
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ earthLayer: [],
|
|
|
+ userClickLeft: 0,
|
|
|
+ userClickTop: 0,
|
|
|
+ tagMsg: null,
|
|
|
+ systemInfoTimmer: null,
|
|
|
+ showDevInfoBox: true,
|
|
|
+ fps: "", // 设备帧率
|
|
|
+ ms: "", // 设备响应时间
|
|
|
+ jsHeapSize: "", // 内存占用
|
|
|
+ gVendor: "",
|
|
|
+ gRenderer: "",
|
|
|
};
|
|
|
},
|
|
|
|
|
|
mounted() {
|
|
|
+ this.initEventListener();
|
|
|
this.initCesium();
|
|
|
+ if (this.showDevInfoBox) {
|
|
|
+ this.initSystemInfo();
|
|
|
+ }
|
|
|
},
|
|
|
|
|
|
unmounted() {
|
|
|
@@ -78,45 +191,66 @@ export default {
|
|
|
clearInterval(this.windLayerTimmer);
|
|
|
this.windLayer.removeLines();
|
|
|
this.windLayer = null;
|
|
|
+ this.windLayerTimmer = null;
|
|
|
}
|
|
|
+
|
|
|
+ clearInterval(this.systemInfoTimmer);
|
|
|
+ this.systemInfoTimmer = null;
|
|
|
},
|
|
|
|
|
|
methods: {
|
|
|
- initCesium() {
|
|
|
+ // 初始化一些监听事件
|
|
|
+ initEventListener() {
|
|
|
+ const mapBox = document.querySelector(".mapBox");
|
|
|
+ mapBox.addEventListener("click", (e) => {
|
|
|
+ const rect = mapBox.getBoundingClientRect();
|
|
|
+ this.userClickLeft = (e.clientX - rect.left).toFixed(0);
|
|
|
+ this.userClickTop = (e.clientY - rect.top + 20).toFixed(0);
|
|
|
+ });
|
|
|
+ },
|
|
|
+
|
|
|
+ // 初始化地球
|
|
|
+ async initCesium() {
|
|
|
// 需要从 https://cesium.com/ion/signup 获取
|
|
|
Cesium.Ion.defaultAccessToken =
|
|
|
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiIwYTQwNDk3MC05YTZkLTQ2ZTEtODc0MS1lZTFkYjFlOTFmNmQiLCJpZCI6MTcyNDQ1LCJpYXQiOjE3NTQ4ODA4MzF9.KnhENYiHxNwTkhTWRA-lHqG59coLVT2FsIyOru2TV3E";
|
|
|
+
|
|
|
+ // 修改 Cesium 默认地图视角为宁夏,狗东西没效果不知道为什么
|
|
|
+ // Cesium.Camera.DEFAULT_VIEW_RACTANGLE = Cesium.Rectangle.fromDegrees(
|
|
|
+ // 104.17,
|
|
|
+ // 35.14,
|
|
|
+ // 107.72,
|
|
|
+ // 39.23
|
|
|
+ // );
|
|
|
+
|
|
|
const viewer = new Cesium.Viewer("cesiumContainer", {
|
|
|
- baseLayerPicker: false,
|
|
|
- animation: false,
|
|
|
+ geocoder: false, // 地址搜索控件
|
|
|
+ homeButton: false, // 返回地图初始位置控件
|
|
|
+ infoBox: false, // 地图默认的信息控件
|
|
|
+ sceneModePicker: false, // 场景模式切换控件
|
|
|
+ baseLayerPicker: false, // 底图切换控件
|
|
|
+ navigationHelpButton: false, // 帮助控件
|
|
|
+ animation: false, // 动画控制控件
|
|
|
+ timeline: false, // 时间线控件
|
|
|
+ fullscreenButton: false, // 全屏按钮控件
|
|
|
+ imageryProvider: false, // 是否显示 Cesium 默认地图的底图
|
|
|
vrButton: false,
|
|
|
- geocoder: false,
|
|
|
- homeButton: false,
|
|
|
- infoBox: false,
|
|
|
- sceneModePicker: false,
|
|
|
selectionIndicator: false,
|
|
|
- timeline: false,
|
|
|
- fullscreenButton: false,
|
|
|
- navigationHelpButton: false,
|
|
|
shouldAnimate: true,
|
|
|
+ // terrainProvider: await Cesium.createWorldTerrainAsync({
|
|
|
+ // requestVertexNormals: true,
|
|
|
+ // requestWaterMask: true,
|
|
|
+ // }),
|
|
|
+ // terrainProvider: new Cesium.CesiumTerrainProvider({
|
|
|
+ // url: "/static/layer.json", // 对应 public/terrain-data 目录
|
|
|
+ // requestVertexNormals: true, // 保留法线数据(光照效果)
|
|
|
+ // requestWaterMask: false, // 本地地形通常无水面效果(需自定义)
|
|
|
+ // }),
|
|
|
});
|
|
|
|
|
|
- // 隐藏logo
|
|
|
- // this.$nextTick(() => {
|
|
|
- // document.querySelector(".cesium-viewer-bottom")?.remove();
|
|
|
- // });
|
|
|
-
|
|
|
- // 隐藏logo
|
|
|
+ // 隐藏 Cesium Logo
|
|
|
viewer.cesiumWidget.creditContainer.style.display = "none";
|
|
|
|
|
|
- // 添加中文底图
|
|
|
- const imageryProvider = new Cesium.UrlTemplateImageryProvider({
|
|
|
- url: "https://webst02.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}",
|
|
|
- credit: "高德地图",
|
|
|
- });
|
|
|
-
|
|
|
- viewer.imageryLayers.addImageryProvider(imageryProvider);
|
|
|
-
|
|
|
// 添加一些3D模型
|
|
|
// const addModel = (name, lon, lat, height) => {
|
|
|
// viewer.entities.add({
|
|
|
@@ -134,48 +268,357 @@ export default {
|
|
|
|
|
|
// 添加点击事件显示坐标
|
|
|
viewer.screenSpaceEventHandler.setInputAction((movement) => {
|
|
|
- const cartesian = viewer.camera.pickEllipsoid(
|
|
|
- movement.position,
|
|
|
- viewer.scene.globe.ellipsoid
|
|
|
- );
|
|
|
- if (cartesian) {
|
|
|
- const cartographic = Cesium.Cartographic.fromCartesian(cartesian);
|
|
|
- const lon = Cesium.Math.toDegrees(cartographic.longitude).toFixed(5);
|
|
|
- const lat = Cesium.Math.toDegrees(cartographic.latitude).toFixed(5);
|
|
|
-
|
|
|
- viewer.entities.removeAll();
|
|
|
- viewer.entities.add({
|
|
|
- position: cartesian,
|
|
|
- point: {
|
|
|
- pixelSize: 10,
|
|
|
- color: Cesium.Color.RED,
|
|
|
- },
|
|
|
- label: {
|
|
|
- text: `经度: ${lon}°, 纬度: ${lat}°`,
|
|
|
- font: '16px "Microsoft YaHei"',
|
|
|
- fillColor: Cesium.Color.WHITE,
|
|
|
- outlineColor: Cesium.Color.BLACK,
|
|
|
- outlineWidth: 2,
|
|
|
- style: Cesium.LabelStyle.FILL_AND_OUTLINE,
|
|
|
- pixelOffset: new Cesium.Cartesian2(0, -30),
|
|
|
+ try {
|
|
|
+ const ray = viewer.camera.getPickRay(movement.position);
|
|
|
+ if (!ray) {
|
|
|
+ this.tagMsg = null;
|
|
|
+ console.log("无法获取射线");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const position = viewer.scene.globe.pick(ray, viewer.scene);
|
|
|
+ if (!position) {
|
|
|
+ this.tagMsg = null;
|
|
|
+ console.log("未找到地球表面交点");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const cartographic = Cesium.Cartographic.fromCartesian(position);
|
|
|
+ if (!cartographic) {
|
|
|
+ this.tagMsg = null;
|
|
|
+ console.log("坐标转换失败");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const level = this.calculateTileLevel(viewer);
|
|
|
+ const tilingScheme = new Cesium.WebMercatorTilingScheme();
|
|
|
+
|
|
|
+ // 确保tilingScheme有positionToTileXY方法
|
|
|
+ if (!tilingScheme.positionToTileXY) {
|
|
|
+ console.error("tilingScheme没有positionToTileXY方法");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!this.cloudLayer) {
|
|
|
+ this.tagMsg = null;
|
|
|
+ } else {
|
|
|
+ this.tagMsg = "";
|
|
|
+ }
|
|
|
+
|
|
|
+ const tileXY = tilingScheme.positionToTileXY(cartographic, level);
|
|
|
+
|
|
|
+ this.checkMode &&
|
|
|
+ console.log(
|
|
|
+ `瓦片坐标: 级别=${level}, X=${tileXY.x}, Y=${tileXY.y}`
|
|
|
+ );
|
|
|
+
|
|
|
+ // const mapSelect = this.basicMapList.find((ele) => {
|
|
|
+ // return ele.id === this.basicMapId;
|
|
|
+ // });
|
|
|
+
|
|
|
+ const clickTileUrl = this.replaceTemplate(
|
|
|
+ // mapSelect.url,
|
|
|
+ "https://tile.openweathermap.org/map/clouds_new/{z}/{x}/{y}.png?appid=3b66d35579770393051599f8d518df4a",
|
|
|
+ level,
|
|
|
+ tileXY
|
|
|
+ );
|
|
|
+
|
|
|
+ this.checkMode && console.log(`用户点击位置瓦片url: ${clickTileUrl}`);
|
|
|
+
|
|
|
+ // 存储当前瓦片信息
|
|
|
+ const layer = viewer.imageryLayers.get(0);
|
|
|
+ const provider = layer.imageryProvider;
|
|
|
+ const currentTile = {
|
|
|
+ x: tileXY.x,
|
|
|
+ y: tileXY.y,
|
|
|
+ level: level,
|
|
|
+ rectangle: tilingScheme.tileXYToRectangle(
|
|
|
+ tileXY.x,
|
|
|
+ tileXY.y,
|
|
|
+ level
|
|
|
+ ),
|
|
|
+ size: {
|
|
|
+ width: provider.tileWidth || 256,
|
|
|
+ height: provider.tileHeight || 256,
|
|
|
},
|
|
|
- });
|
|
|
+ };
|
|
|
+
|
|
|
+ // 计算并显示在瓦片内的位置
|
|
|
+ const clickPos = this.calculateTilePosition(
|
|
|
+ cartographic,
|
|
|
+ currentTile
|
|
|
+ );
|
|
|
+
|
|
|
+ if (this.cloudLayer) {
|
|
|
+ this.getTileImageOpacity(
|
|
|
+ clickTileUrl,
|
|
|
+ clickPos.pixelX,
|
|
|
+ clickPos.pixelY
|
|
|
+ ).then((imgSource) => {
|
|
|
+ const {
|
|
|
+ rawAlpha, // 原始透明度值 (0-255)
|
|
|
+ alphaPercentage, // 透明度百分比
|
|
|
+ whiteScore, // 白色程度得分 (0-100)
|
|
|
+ } = imgSource;
|
|
|
+ this.checkMode && console.log(111, alphaPercentage);
|
|
|
+ this.tagMsg = `${alphaPercentage * 2}%`;
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ if (this.checkMode) {
|
|
|
+ const tileRectangle = tilingScheme.tileXYToRectangle(
|
|
|
+ tileXY.x,
|
|
|
+ tileXY.y,
|
|
|
+ level
|
|
|
+ );
|
|
|
+ this.highlightTile(viewer, tileRectangle);
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error("获取瓦片时出错:", error);
|
|
|
}
|
|
|
+
|
|
|
+ return;
|
|
|
+
|
|
|
+ // const cartesian = viewer.camera.pickEllipsoid(
|
|
|
+ // movement.position,
|
|
|
+ // viewer.scene.globe.ellipsoid
|
|
|
+ // );
|
|
|
+ // if (cartesian) {
|
|
|
+ // const cartographic = Cesium.Cartographic.fromCartesian(cartesian);
|
|
|
+ // const lon = Cesium.Math.toDegrees(cartographic.longitude).toFixed(5);
|
|
|
+ // const lat = Cesium.Math.toDegrees(cartographic.latitude).toFixed(5);
|
|
|
+
|
|
|
+ // viewer.entities.removeAll();
|
|
|
+ // viewer.entities.add({
|
|
|
+ // position: cartesian,
|
|
|
+ // point: {
|
|
|
+ // pixelSize: 10,
|
|
|
+ // color: Cesium.Color.RED,
|
|
|
+ // },
|
|
|
+ // label: {
|
|
|
+ // text: `经度: ${lon}°, 纬度: ${lat}°`,
|
|
|
+ // font: '16px "Microsoft YaHei"',
|
|
|
+ // fillColor: Cesium.Color.WHITE,
|
|
|
+ // outlineColor: Cesium.Color.BLACK,
|
|
|
+ // outlineWidth: 2,
|
|
|
+ // style: Cesium.LabelStyle.FILL_AND_OUTLINE,
|
|
|
+ // pixelOffset: new Cesium.Cartesian2(0, -30),
|
|
|
+ // },
|
|
|
+ // });
|
|
|
+ // }
|
|
|
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
|
|
|
|
|
|
+ // 监听鼠标滚轮事件
|
|
|
+ viewer.screenSpaceEventHandler.setInputAction((wheelment) => {
|
|
|
+ this.tagMsg = null;
|
|
|
+ //从Cesium中获取当前地图瓦片等级
|
|
|
+ // let tiles = new Set();
|
|
|
+ // let tilesToRender = viewer.scene.globe._surface._tilesToRender;
|
|
|
+ // if (Cesium.defined(tilesToRender)) {
|
|
|
+ // for (let i = 0; i < tilesToRender.length; i++) {
|
|
|
+ // tiles.add(tilesToRender[i].level);
|
|
|
+ // }
|
|
|
+ // console.log("当前地图瓦片级别为:");
|
|
|
+ // console.log(tiles);
|
|
|
+ // }
|
|
|
+ }, Cesium.ScreenSpaceEventType.WHEEL);
|
|
|
+
|
|
|
this.viewer = viewer;
|
|
|
|
|
|
+ this.setMapImageryProvider();
|
|
|
this.initGeoJsonData();
|
|
|
},
|
|
|
|
|
|
+ // 初始化性能监控
|
|
|
+ initSystemInfo() {
|
|
|
+ this.viewer.scene.debugShowFramesPerSecond = false;
|
|
|
+
|
|
|
+ // 性能监控变量
|
|
|
+ let lastFrameTime = performance.now();
|
|
|
+ let frameCount = 0;
|
|
|
+ let fps = 0;
|
|
|
+ let frameTime = 0;
|
|
|
+
|
|
|
+ // 获取帧率与响应时长
|
|
|
+ this.viewer.scene.postRender.addEventListener(() => {
|
|
|
+ const now = performance.now();
|
|
|
+ const delta = now - lastFrameTime;
|
|
|
+
|
|
|
+ frameCount++;
|
|
|
+
|
|
|
+ // 每秒更新一次数据(避免更新太频繁)
|
|
|
+ if (delta >= 1000) {
|
|
|
+ fps = Math.round((frameCount * 1000) / delta);
|
|
|
+ frameTime = delta / frameCount;
|
|
|
+
|
|
|
+ // 更新显示
|
|
|
+ this.fps = `${fps} FPS`;
|
|
|
+ this.ms = `${frameTime.toFixed(1) + " ms"}`;
|
|
|
+
|
|
|
+ // 重置计数器
|
|
|
+ frameCount = 0;
|
|
|
+ lastFrameTime = now;
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ // 获取内存占用
|
|
|
+ if (window.performance && performance.memory) {
|
|
|
+ const jsHeapSize = performance.memory.usedJSHeapSize / 1048576;
|
|
|
+ this.jsHeapSize = `${parseInt(jsHeapSize)} MB`;
|
|
|
+ // const memory = performance.memory;
|
|
|
+ // console.log("已分配堆内存:", memory.totalJSHeapSize / 1048576 + " MB");
|
|
|
+ // console.log("已使用堆内存:", memory.usedJSHeapSize / 1048576 + " MB");
|
|
|
+ // console.log("堆内存限制:", memory.jsHeapSizeLimit / 1048576 + " MB");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取显卡信息
|
|
|
+ const canvas = document.createElement("canvas");
|
|
|
+ const gl = canvas.getContext("webgl");
|
|
|
+ if (gl) {
|
|
|
+ const debugInfo = gl.getExtension("WEBGL_debug_renderer_info");
|
|
|
+ if (debugInfo) {
|
|
|
+ const gVendor = gl.getParameter(debugInfo.UNMASKED_VENDOR_WEBGL);
|
|
|
+ const gRenderer = gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL);
|
|
|
+
|
|
|
+ // console.log("GPU厂商:", gVendor);
|
|
|
+ // console.log("GPU型号:", gRenderer);
|
|
|
+
|
|
|
+ this.gVendor = gVendor;
|
|
|
+ this.gRenderer = gRenderer.split(",")?.[1]
|
|
|
+ ? gRenderer.split(",")?.[1]
|
|
|
+ : "";
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // 计算瓦片级别的辅助函数
|
|
|
+ calculateTileLevel(viewer) {
|
|
|
+ // 方法1:根据相机高度估算
|
|
|
+ const height = viewer.camera.positionCartographic.height;
|
|
|
+ if (!height) return 12; // 默认值
|
|
|
+
|
|
|
+ // 高度与级别的近似关系(根据实际需求调整)
|
|
|
+ const level = Math.floor(20 - Math.log(height / 1000) / Math.log(2));
|
|
|
+ return Math.max(0, Math.min(18, level)); // 限制在0-18级之间
|
|
|
+
|
|
|
+ // 方法2:使用当前视图的细节层次
|
|
|
+ // return viewer.scene.globe.maximumScreenSpaceError;
|
|
|
+ },
|
|
|
+
|
|
|
+ // 高亮显示瓦片的辅助函数
|
|
|
+ highlightTile(viewer, rectangle) {
|
|
|
+ // 移除之前的高亮
|
|
|
+ viewer.entities.removeById("highlighted-tile");
|
|
|
+
|
|
|
+ // 添加新的高亮
|
|
|
+ viewer.entities.add({
|
|
|
+ id: "highlighted-tile",
|
|
|
+ rectangle: {
|
|
|
+ coordinates: rectangle,
|
|
|
+ material: Cesium.Color.RED.withAlpha(0.3),
|
|
|
+ outline: true,
|
|
|
+ outlineColor: Cesium.Color.RED,
|
|
|
+ outlineWidth: 2,
|
|
|
+ },
|
|
|
+ });
|
|
|
+ },
|
|
|
+
|
|
|
+ // 计算在瓦片内的位置
|
|
|
+ calculateTilePosition(cartographic, tile) {
|
|
|
+ const rect = tile.rectangle;
|
|
|
+ const size = tile.size;
|
|
|
+
|
|
|
+ // 计算在瓦片内的归一化位置
|
|
|
+ const lonNormalized =
|
|
|
+ (cartographic.longitude - rect.west) / (rect.east - rect.west);
|
|
|
+ const latNormalized =
|
|
|
+ (cartographic.latitude - rect.south) / (rect.north - rect.south);
|
|
|
+
|
|
|
+ // 转换为像素坐标(原点在左上角)
|
|
|
+ const pixelX = Math.floor(lonNormalized * size.width);
|
|
|
+ const pixelY = Math.floor((1 - latNormalized) * size.height); // 翻转Y轴
|
|
|
+
|
|
|
+ this.checkMode && console.log(`left:${pixelX},top:${pixelY}`);
|
|
|
+ return { pixelX, pixelY };
|
|
|
+ },
|
|
|
+
|
|
|
+ // canvas 获取地图瓦片颜色信息
|
|
|
+ async getTileImageOpacity(imageUrl, left, top) {
|
|
|
+ // 1. 创建临时图像加载网络图片
|
|
|
+ const img = new Image();
|
|
|
+ img.crossOrigin = "Anonymous"; // 解决跨域问题
|
|
|
+ img.src = imageUrl;
|
|
|
+
|
|
|
+ // 2. 图片加载完成后处理
|
|
|
+ await new Promise((resolve) => (img.onload = resolve));
|
|
|
+
|
|
|
+ // 3. 创建Canvas并绘制图像
|
|
|
+ const canvas = document.createElement("canvas");
|
|
|
+ canvas.width = img.width;
|
|
|
+ canvas.height = img.height;
|
|
|
+ const ctx = canvas.getContext("2d");
|
|
|
+ ctx.drawImage(img, 0, 0);
|
|
|
+
|
|
|
+ // 4. 获取鼠标点击位置的像素数据
|
|
|
+ const pixelData = ctx.getImageData(left, top, 1, 1).data;
|
|
|
+ const [r, g, b, alpha] = pixelData;
|
|
|
+
|
|
|
+ // 5. 计算白色程度(RGB接近255的程度)
|
|
|
+ const whiteRatio = (r + g + b) / (3 * 255); // RGB平均值归一化
|
|
|
+ const whiteScore = Math.round(whiteRatio * 100); // 映射到0-100
|
|
|
+
|
|
|
+ // 6. 返回结果
|
|
|
+ return {
|
|
|
+ rawAlpha: alpha, // 原始透明度值 (0-255)
|
|
|
+ alphaPercentage: Math.round((alpha / 255) * 100), // 透明度百分比
|
|
|
+ whiteScore: whiteScore, // 白色程度得分 (0-100)
|
|
|
+ };
|
|
|
+ },
|
|
|
+
|
|
|
+ // 替换底图 xyz 值为鼠标点击位置的值并返回
|
|
|
+ replaceTemplate(str, level, tileXY) {
|
|
|
+ return str.replace(/\{([xyz])\}/g, (match, key) => {
|
|
|
+ switch (key) {
|
|
|
+ case "x":
|
|
|
+ return tileXY.x;
|
|
|
+ case "y":
|
|
|
+ return tileXY.y;
|
|
|
+ case "z":
|
|
|
+ return level;
|
|
|
+ default:
|
|
|
+ return match; // 未匹配时返回原内容(理论上不会执行)
|
|
|
+ }
|
|
|
+ });
|
|
|
+ },
|
|
|
+
|
|
|
+ // 设置地球底图
|
|
|
+ setMapImageryProvider() {
|
|
|
+ if (this.imageryProvider) {
|
|
|
+ this.viewer.imageryLayers.remove(this.imageryProvider);
|
|
|
+ this.imageryProvider = null;
|
|
|
+ }
|
|
|
+
|
|
|
+ const imageryProvider = this.basicMapList.find((ele) => {
|
|
|
+ return ele.id === this.basicMapId;
|
|
|
+ });
|
|
|
+
|
|
|
+ this.imageryProvider = new Cesium.UrlTemplateImageryProvider(
|
|
|
+ imageryProvider
|
|
|
+ );
|
|
|
+
|
|
|
+ // 添加底图
|
|
|
+ this.viewer.imageryLayers.addImageryProvider(this.imageryProvider);
|
|
|
+ },
|
|
|
+
|
|
|
// 初始化 geoJson 数据
|
|
|
async initGeoJsonData() {
|
|
|
// 创建GeoJSON数据源
|
|
|
await new Cesium.GeoJsonDataSource.load(basicGeoJson, {
|
|
|
stroke: Cesium.Color.WHITE, // 边界线颜色
|
|
|
- fill: Cesium.Color.BLACK.withAlpha(0.1), // 填充颜色
|
|
|
+ fill: Cesium.Color.BLACK.withAlpha(0), // 填充颜色
|
|
|
strokeWidth: 1, // 边界线宽度
|
|
|
markerSymbol: "?", // 点要素的符号
|
|
|
+ clampToGround: true, // 贴地
|
|
|
}).then((dataSource) => {
|
|
|
// 添加到视图
|
|
|
this.viewer.dataSources.add(dataSource);
|
|
|
@@ -183,9 +626,7 @@ export default {
|
|
|
for (let i = 0; i < entities.length; i++) {
|
|
|
let entity = entities[i];
|
|
|
|
|
|
- let polyPositions = entity.polygon.hierarchy.getValue(
|
|
|
- Cesium.JulianDate.now()
|
|
|
- ).positions;
|
|
|
+ entity.polygon.hierarchy.getValue(Cesium.JulianDate.now()).positions;
|
|
|
//单独设置线条样式
|
|
|
var positions = entity.polygon.hierarchy._value.positions;
|
|
|
|
|
|
@@ -211,9 +652,10 @@ export default {
|
|
|
|
|
|
cities.forEach((city) => {
|
|
|
labelLayer.add({
|
|
|
- position: Cesium.Cartesian3.fromDegrees(city.lon, city.lat),
|
|
|
+ name: "cityLabel",
|
|
|
+ position: Cesium.Cartesian3.fromDegrees(city.lon, city.lat, 10),
|
|
|
text: city.name,
|
|
|
- font: 'bold 16px "Microsoft YaHei", sans-serif',
|
|
|
+ font: 'bold 14px "Microsoft YaHei", sans-serif',
|
|
|
fillColor: Cesium.Color.YELLOW,
|
|
|
outlineColor: Cesium.Color.BLACK,
|
|
|
outlineWidth: 2,
|
|
|
@@ -268,6 +710,20 @@ export default {
|
|
|
};
|
|
|
},
|
|
|
|
|
|
+ // 获取当前地图瓦片级别(暂时没用到怀疑有BUG)
|
|
|
+ getTileLevel() {
|
|
|
+ let tiles = new Set();
|
|
|
+ let tilesToRender = this.viewer.scene.globe._surface._tilesToRender;
|
|
|
+ if (Cesium.defined(tilesToRender)) {
|
|
|
+ for (let i = 0; i < tilesToRender.length; i++) {
|
|
|
+ tiles.add(tilesToRender[i].level);
|
|
|
+ }
|
|
|
+ return [...tiles].sort((a, b) => {
|
|
|
+ return b - a;
|
|
|
+ })[0];
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
// 显示云图
|
|
|
showCloudLayer() {
|
|
|
// 设置云图位置(示例:覆盖中国区域)
|
|
|
@@ -278,23 +734,31 @@ export default {
|
|
|
// 53.55 // 北纬
|
|
|
// );
|
|
|
|
|
|
- const now = new Date();
|
|
|
- const year = now.getFullYear();
|
|
|
- const month = String(now.getMonth() + 1).padStart(2, "0");
|
|
|
- const day = String(now.getDate()).padStart(2, "0");
|
|
|
- const hour = String(Math.floor(now.getHours() / 3) * 3).padStart(2, "0"); // 每3小时更新
|
|
|
+ // const now = new Date();
|
|
|
+ // const year = now.getFullYear();
|
|
|
+ // const month = String(now.getMonth() + 1).padStart(2, "0");
|
|
|
+ // const day = String(now.getDate()).padStart(2, "0");
|
|
|
+ // const hour = String(Math.floor(now.getHours() / 3) * 3).padStart(2, "0"); // 每3小时更新
|
|
|
+
|
|
|
+ // // 创建云图图层
|
|
|
+ // const cloudLayer = this.viewer.imageryLayers.addImageryProvider(
|
|
|
+ // new Cesium.UrlTemplateImageryProvider({
|
|
|
+ // url: `https://data.ventusky.com/${year}/${month}/${day}/icon/whole_world/hour_${hour}/icon_oblacnost_${year}${month}${day}_${hour}.jpg`,
|
|
|
+ // rectangle: Cesium.Rectangle.fromDegrees(-180, -90, 180, 90),
|
|
|
+ // credit: "实时卫星云图",
|
|
|
+ // })
|
|
|
+ // );
|
|
|
|
|
|
- // 创建云图图层
|
|
|
+ // 调用云层底图
|
|
|
const cloudLayer = this.viewer.imageryLayers.addImageryProvider(
|
|
|
new Cesium.UrlTemplateImageryProvider({
|
|
|
- url: `https://data.ventusky.com/${year}/${month}/${day}/icon/whole_world/hour_${hour}/icon_oblacnost_${year}${month}${day}_${hour}.jpg`,
|
|
|
- rectangle: Cesium.Rectangle.fromDegrees(-180, -90, 180, 90),
|
|
|
- credit: "实时卫星云图",
|
|
|
+ url: "https://tile.openweathermap.org/map/clouds_new/{z}/{x}/{y}.png?appid=3b66d35579770393051599f8d518df4a",
|
|
|
+ credit: "云层影像地图",
|
|
|
})
|
|
|
);
|
|
|
|
|
|
// 设置蓝色调效果
|
|
|
- cloudLayer.alpha = 0.75; // 透明度
|
|
|
+ cloudLayer.alpha = 1; // 透明度
|
|
|
cloudLayer.brightness = 1; // 亮度
|
|
|
cloudLayer.contrast = 1; // 对比度
|
|
|
|
|
|
@@ -314,6 +778,7 @@ export default {
|
|
|
// 移除卫星云图
|
|
|
removeCloudLayer() {
|
|
|
if (this.cloudLayer) {
|
|
|
+ this.tagMsg = null;
|
|
|
this.viewer.imageryLayers.remove(this.cloudLayer);
|
|
|
this.cloudLayer = null;
|
|
|
}
|
|
|
@@ -328,25 +793,24 @@ export default {
|
|
|
}
|
|
|
},
|
|
|
|
|
|
- // 切换微星图显隐
|
|
|
- // switchCloudLayer() {
|
|
|
- // if (this.cloudLayer) {
|
|
|
- // this.removeCloudLayer();
|
|
|
- // } else {
|
|
|
- // this.showCloudLayer();
|
|
|
- // }
|
|
|
- // },
|
|
|
-
|
|
|
+ // 切换卫星云图显隐
|
|
|
switchCloudLayer() {
|
|
|
- this.$router.push({
|
|
|
- path: "/cloudLayer",
|
|
|
- });
|
|
|
+ // this.$router.push({
|
|
|
+ // path: "/cloudLayer",
|
|
|
+ // });
|
|
|
+ if (this.cloudLayer) {
|
|
|
+ this.removeCloudLayer();
|
|
|
+ } else {
|
|
|
+ this.showCloudLayer();
|
|
|
+ }
|
|
|
},
|
|
|
+
|
|
|
switchRainLayer() {
|
|
|
this.$router.push({
|
|
|
path: "/rainLayer",
|
|
|
});
|
|
|
},
|
|
|
+
|
|
|
switchTemperatureLayerr() {
|
|
|
this.$router.push({
|
|
|
path: "/temperatureLayer",
|
|
|
@@ -362,6 +826,7 @@ export default {
|
|
|
height: 100%;
|
|
|
position: relative;
|
|
|
box-sizing: content-box;
|
|
|
+ position: relative;
|
|
|
|
|
|
.menuBox {
|
|
|
position: absolute;
|
|
|
@@ -374,6 +839,15 @@ export default {
|
|
|
align-items: center;
|
|
|
padding: 10px;
|
|
|
|
|
|
+ .el-button {
|
|
|
+ margin: 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ .item {
|
|
|
+ font-size: 12px;
|
|
|
+ margin-left: 10px;
|
|
|
+ }
|
|
|
+
|
|
|
&.switch {
|
|
|
opacity: 0;
|
|
|
transition: 0.2s;
|
|
|
@@ -384,5 +858,44 @@ export default {
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ .tag {
|
|
|
+ position: absolute;
|
|
|
+ left: 0;
|
|
|
+ top: 0;
|
|
|
+ background: #fff;
|
|
|
+ font-size: 12px;
|
|
|
+ color: #000;
|
|
|
+ padding: 4px 8px;
|
|
|
+ border-radius: 28px;
|
|
|
+ transform: translate(-50%, -50%);
|
|
|
+ transition: 0.1s;
|
|
|
+ pointer-events: none;
|
|
|
+ }
|
|
|
+
|
|
|
+ .devInfoBox {
|
|
|
+ width: 130px;
|
|
|
+ position: absolute;
|
|
|
+ right: 0;
|
|
|
+ bottom: 0;
|
|
|
+ padding: 4px;
|
|
|
+ font-size: 12px;
|
|
|
+ color: #fff;
|
|
|
+ background: rgba(0, 0, 0, 0.25);
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ justify-content: flex-start;
|
|
|
+ align-items: flex-start;
|
|
|
+ z-index: 500;
|
|
|
+ user-select: none;
|
|
|
+
|
|
|
+ .item {
|
|
|
+ width: 100%;
|
|
|
+ margin-top: 4px;
|
|
|
+ overflow: hidden;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ white-space: nowrap;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
</style>
|