<template>
|
<div id="mars3dContainer" class="mars3d-container" style="height: 93%"></div>
|
</template>
|
|
<script setup lang="ts">
|
import { onMounted, onBeforeUnmount, watch, defineProps, defineEmits, ref } from "vue";
|
import { FindScreenFence } from "@/api/modules/hxzk/fence/fence";
|
import { FindScreenAnchor } from "@/api/modules/hxzk/device/anchor";
|
|
import { ElMessageBox, ElMessage, ElNotification } from "element-plus";
|
import anchor from "@/views/hxzk/images/map/anchor.png";
|
import * as mars3d from "mars3d";
|
import "mars3d-space";
|
import { getThreeModel } from "@/api/modules/hxzk/map/threemap";
|
import "mars3d-cesium/Build/Cesium/Widgets/widgets.css";
|
import "mars3d/mars3d.css";
|
import { editThreeMap } from "@/api/modules/hxzk/map/threemap";
|
import { getThreeMapConfig } from "@/api/modules/hxzk/map/threemap";
|
// 定义props
|
const props = defineProps({
|
mapconfig: {
|
type: Object,
|
required: true
|
},
|
showfence: {
|
type: Boolean,
|
default: false
|
},
|
showanchor: {
|
type: Boolean,
|
default: false
|
},
|
showlayer: {
|
type: Boolean,
|
default: false
|
},
|
showthreeMap: {
|
type: Boolean,
|
default: true
|
},
|
mapcolor: {
|
type: String
|
}
|
});
|
|
// 定义emits
|
const emit = defineEmits(["mapLoaded"]); // 使用驼峰式命名
|
|
let map;
|
|
let graphicLayerFence;
|
let graphicLayerAnchor;
|
let xyztileLayer;
|
let tiles3dLayer;
|
const baseColor = ref("");
|
// 初始化地图
|
const initMap = () => {
|
getThreeMapConfig().then(mapConfig => {
|
baseColor.value = mapConfig.mapcolor;
|
map = new mars3d.Map("mars3dContainer", {
|
scene: {
|
globe: {
|
show: true,
|
baseColor: "#679AB2"
|
},
|
center: {
|
lat: props.mapconfig.mapposition.split(",")[1],
|
lng: props.mapconfig.mapposition.split(",")[0],
|
alt: props.mapconfig.alt,
|
heading: props.mapconfig.heading,
|
pitch: props.mapconfig.pitch
|
},
|
showSkyAtmosphere: false,
|
skyBox: {
|
type: "ground",
|
sources: {
|
positiveX: `${baseUrl}/uploads/Map/Three/0.png`,
|
negativeX: `${baseUrl}/uploads/Map/Three/1.png`,
|
positiveY: `${baseUrl}/uploads/Map/Three/4.png`,
|
negativeY: `${baseUrl}/uploads/Map/Three/5.png`,
|
positiveZ: `${baseUrl}/uploads/Map/Three/2.png`,
|
negativeZ: `${baseUrl}/uploads/Map/Three/3.png`
|
}
|
}
|
},
|
basemaps: [
|
// {
|
// name: "高德影像",
|
// type: "group",
|
// layers: [
|
// { name: "底图", type: "gaode", layer: "img_d" },
|
// { name: "注记", type: "gaode", layer: "img_z" }
|
// ]
|
// },
|
{
|
id: 8890,
|
name: "谷歌影像",
|
icon: "https://data.mars3d.cn/img/thumbnail/basemap/google_img.png",
|
type: "google",
|
// layer: "img_d", //内置的URL失效时,可以按下面类似自定义其他URL的谷歌服务
|
url: "https://cbk.8ditu.com/maps/vt?lyrs=s&v=982&gl=cn&x={x}&y={y}&z={z}",
|
chinaCRS: mars3d.ChinaCRS.GCJ02,
|
show: true
|
},
|
// {
|
// name: "高德电子",
|
// type: "gaode",
|
// layer: "vec"
|
// },
|
// {
|
// name: "百度影像",
|
// icon: "/hxzkuwb/view/Home/img/basemaps/gaode_img.png",
|
// type: "group",
|
// layers: [
|
// { name: "底图", type: "baidu", layer: "img_d" },
|
// { name: "注记", type: "baidu", layer: "img_z" }
|
// ]
|
// },
|
// {
|
// name: "百度电子",
|
// icon: "/hxzkuwb/view/Home/img/basemaps/gaode_vec.png",
|
// type: "baidu",
|
// layer: "vec"
|
// },
|
// {
|
// name: "ArcGIS影像",
|
// icon: "/hxzkuwb/view/Home/img/basemaps/esriWorldImagery.png",
|
// type: "arcgis",
|
// layer: "img_d",
|
// enablePickFeatures: false
|
// },
|
{
|
id: 2017,
|
pid: 10,
|
name: "蓝色底图",
|
icon: "/IAFService/view/Home/demo/img/basemaps/bd-c-midnight.png",
|
type: "gaode",
|
layer: "vec",
|
chinaCRS: "GCJ02",
|
invertColor: true,
|
filterColor: baseColor.value || "blue",
|
brightness: 0.6,
|
contrast: 2,
|
gamma: 1,
|
hue: 1,
|
saturation: 0,
|
show: true
|
},
|
{
|
id: 9001,
|
name: "天地图影像(EPSG:3857)",
|
icon: "https://data.mars3d.cn/img/thumbnail/basemap/tdt_img.png",
|
type: "tdt",
|
layer: "img_d",
|
key: mars3d.Token.tiandituArr
|
}
|
],
|
control: {}
|
});
|
// 设置初始时间
|
let currentTime = new Date();
|
let newTime = new Date(currentTime.getTime() - 5000);
|
map.clock.currentTime = mars3d.Cesium.JulianDate.fromDate(new Date(newTime));
|
map.unbindContextMenu();
|
AddMenu();
|
// 触发地图加载完成事件
|
emit("mapLoaded", map);
|
// 初始化图层
|
|
graphicLayerFence = new mars3d.layer.GraphicLayer();
|
map.addLayer(graphicLayerFence);
|
|
graphicLayerAnchor = new mars3d.layer.GraphicLayer();
|
map.addLayer(graphicLayerAnchor);
|
|
// 根据props初始化显示状态
|
if (props.showfence) AddFence();
|
if (props.showanchor) AddAnchor();
|
if (props.showlayer) Addlayer();
|
if (props.showthreeMap) AddThreeMap();
|
});
|
};
|
|
// 添加围栏
|
const AddFence = () => {
|
FindScreenFence().then(Fences => {
|
for (let i = 0; i < Fences.length; i++) {
|
let fenceList = Fences[i].baoliu8.split(";");
|
let fence = [];
|
for (let j = 0; j < fenceList.length; j++) {
|
const coordinateString = fenceList[j];
|
const [longitude, latitude] = coordinateString.split(",");
|
fence.push([parseFloat(longitude), parseFloat(latitude)]);
|
}
|
//围栏
|
const graphicFence = new mars3d.graphic.PolygonPrimitive({
|
positions: fence,
|
style: {
|
closure: true,
|
opacity: 0.3,
|
color: Fences[i].baoliu14,
|
diffHeight: Fences[i].baoliu11 / 100,
|
materialType: mars3d.MaterialType.LineFlow,
|
label: {
|
text: Fences[i].name,
|
font_size: 18,
|
outline: true,
|
color: "#ffffff",
|
distanceDisplayCondition: true,
|
distanceDisplayCondition_far: 500000,
|
distanceDisplayCondition_near: 0
|
}
|
}
|
});
|
|
graphicLayerFence.addGraphic(graphicFence);
|
}
|
});
|
};
|
|
// 添加基站
|
const AddAnchor = () => {
|
FindScreenAnchor().then(Anchors => {
|
Anchors.forEach(element => {
|
let Anchor = new mars3d.graphic.BillboardEntity({
|
position: [element.baoliu6, element.baoliu7, 0],
|
style: {
|
image: anchor,
|
horizontalOrigin: mars3d.Cesium.HorizontalOrigin.CENTER,
|
verticalOrigin: mars3d.Cesium.VerticalOrigin.BOTTOM,
|
scale: 0.2,
|
label: {
|
text: "基站编号:" + element.anchorid,
|
font_size: 12,
|
color: "#ffffff",
|
font_weight: "bold",
|
distanceDisplayCondition: false,
|
clampToGround: false,
|
verticalOrigin: mars3d.Cesium.VerticalOrigin.TOP,
|
pixelOffsetY: -60,
|
outline: true
|
}
|
},
|
attr: element
|
});
|
graphicLayerAnchor.addGraphic(Anchor); // 还可以另外一种写法: graphic.addTo(graphicLayer)
|
});
|
});
|
};
|
|
const baseUrl = import.meta.env.VITE_IP;
|
// 添加瓦片图
|
const Addlayer = () => {
|
xyztileLayer = new mars3d.layer.XyzLayer({
|
url: `${baseUrl}/uploads/Map/tiles/{z}/{x}/{y}.png`,
|
minimumLevel: 0,
|
maximumLevel: 23,
|
zIndex: 25
|
});
|
map.addLayer(xyztileLayer);
|
};
|
|
const AddMenu = () => {
|
//显示此处经纬度
|
window._test_callback1 = function (e) {
|
const mpt = mars3d.LngLatPoint.fromCartesian(e.cartesian);
|
const mptStr = `经度:${mpt.lng} , 纬度:${mpt.lat} , 高度:${mpt.alt}`;
|
|
ElMessageBox.alert(mptStr + "", "此处坐标信息");
|
};
|
//设置当前视角为中心
|
window._test_callback2 = function () {
|
const mpt = map.getCameraView();
|
const params = {
|
mapposition: mpt.lng + "," + mpt.lat,
|
alt: mpt.alt,
|
heading: mpt.heading,
|
pitch: mpt.pitch
|
};
|
|
ElMessageBox.confirm("是否要设置当前点位为中心点?", "设置中心点", {
|
confirmButtonText: "确认",
|
cancelButtonText: "取消",
|
draggable: true,
|
overflow: true
|
}).then(() => {
|
editThreeMap(params);
|
ElMessage({
|
type: "success",
|
message: "设置成功"
|
});
|
});
|
};
|
|
const mapContextmenuItems = [
|
{
|
text: "显示此处经纬度",
|
|
callback: "_test_callback1"
|
},
|
{
|
text: "设置当前视角为中心",
|
callback: "_test_callback2"
|
}
|
];
|
map.bindContextMenu(mapContextmenuItems);
|
};
|
|
// 添加三维模型
|
const AddThreeMap = async () => {
|
const params = {
|
departmentId: "",
|
pageNum: 1,
|
pageSize: 10
|
};
|
const Model = await getThreeModel(params);
|
for (let i = 0; i < Model.data.list.length; i++) {
|
tiles3dLayer = new mars3d.layer.TilesetLayer({
|
name: Model.data.list[i].filename,
|
type: "3dtiles",
|
url: `${baseUrl}/uploads/Map/Three/${Model.data.list[i].company}_${Model.data.list[i].filename}/tileset.json`,
|
position: { lng: Model.data.list[i].lng, lat: Model.data.list[i].lat, alt: Model.data.list[i].alt }, // 图层的初始位置,包含经度、纬度和高度
|
maximumScreenSpaceError: 100,
|
maxMemory: 2048, // 最大缓存内存大小(MB)
|
cullWithChildrenBounds: false,
|
skipLevelOfDetail: true,
|
preferLeaves: true,
|
popup: [
|
{ field: "id", name: "编号" },
|
{ field: "name", name: "名称" }
|
]
|
});
|
// 单击事件
|
tiles3dLayer.on(mars3d.EventType.click, function (event) {
|
console.log("单击了3dtiles图层", event);
|
});
|
// tiles3dLayer.bindPopup(function (event) {
|
// console.log(event.layer.options.name);
|
// if (event.layer.options.name != "公司测试模型") {
|
// return;
|
// }
|
// return `
|
// <div style="padding: 20px; min-width: 250px; font-family: Arial, sans-serif;">
|
|
// <div style="display: flex; flex-direction: column; gap: 12px;">
|
// <!-- 建筑图层 -->
|
// <div style="display: flex; align-items: center; gap: 10px; padding: 10px;
|
// background: #f8f9fa; border-radius: 8px;">
|
// <div style="width: 30px; height: 30px;
|
// border-radius: 6px; display: flex; align-items: center;
|
// justify-content: center; color: white;">
|
// 🏢
|
// </div>
|
// <div style="flex: 1;">
|
|
// <div style="font-size: 14px; color: #7f8c8d;">全部</div>
|
// </div>
|
// <button onclick="toggleLayer('全部', this)"
|
// class="layer-toggle">
|
// 查看
|
// </button>
|
// </div>
|
// <!-- 建筑图层 -->
|
// <div style="display: flex; align-items: center; gap: 10px; padding: 10px;
|
// background: #f8f9fa; border-radius: 8px;">
|
// <div style="width: 30px; height: 30px;
|
// border-radius: 6px; display: flex; align-items: center;
|
// justify-content: center; color: white;">
|
// 🏢
|
// </div>
|
// <div style="flex: 1;">
|
|
// <div style="font-size: 14px; color: #7f8c8d;">八层</div>
|
// </div>
|
// <button onclick="toggleLayer('八层', this)"
|
// class="layer-toggle">
|
// 查看
|
// </button>
|
// </div>
|
|
// <!-- 建筑图层 -->
|
// <div style="display: flex; align-items: center; gap: 10px; padding: 10px;
|
// background: #f8f9fa; border-radius: 8px;">
|
// <div style="width: 30px; height: 30px;
|
// border-radius: 6px; display: flex; align-items: center;
|
// justify-content: center; color: white;">
|
// 🏢
|
// </div>
|
// <div style="flex: 1;">
|
// <div style="font-size: 14px; color: #7f8c8d;">一层</div>
|
// </div>
|
// <button onclick="toggleLayer('一层', this)"
|
// class="layer-toggle">
|
// 查看
|
// </button>
|
// </div>
|
// </div>
|
// </div>
|
|
// <style>
|
// .layer-toggle {
|
// padding: 6px 12px;
|
// background: #95a5a6;
|
// color: white;
|
// border: none;
|
// border-radius: 4px;
|
// cursor: pointer;
|
// font-size: 12px;
|
// transition: background 0.3s;
|
// }
|
|
// .layer-toggle.active {
|
// background: #2ecc71;
|
// }
|
|
// .layer-toggle:hover {
|
// opacity: 0.9;
|
// }
|
// </style>
|
// `;
|
// });
|
map.addLayer(tiles3dLayer);
|
tiles3dLayer.readyPromise.then(function (layer) {
|
tiles3dLayer.tileset.loadProgress.addEventListener(function (numberOfPendingRequests, numberOfTilesProcessing) {
|
if (numberOfPendingRequests === 0 && numberOfTilesProcessing === 0) {
|
ElNotification({
|
message: `加载完成`,
|
type: "success",
|
position: "top-left"
|
});
|
return;
|
}
|
|
ElNotification({
|
message: `【${layer.options.name}模型】加载:${numberOfPendingRequests} 处理:${numberOfTilesProcessing}`,
|
type: "success",
|
position: "top-left"
|
});
|
});
|
});
|
}
|
|
// 单击事件
|
tiles3dLayer.on(mars3d.EventType.click, function (event) {
|
if (event.layer.options.name == "华星公司") {
|
}
|
});
|
tiles3dLayer.style = new Cesium.Cesium3DTileStyle({
|
color: {
|
conditions: [
|
["${标高} ===' 0 '", "rgba(255, 200, 200,0)"],
|
["true", "rgba(255, 255, 255,1)"]
|
]
|
}
|
});
|
};
|
window.toggleLayer = function (layerType, buttonElement) {
|
const heightNum = ref(0);
|
const heightNum1 = ref(0);
|
switch (layerType) {
|
case "全部":
|
if (buttonElement) {
|
tiles3dLayer.style = undefined;
|
buttonElement.classList.toggle("active", tiles3dLayer.show);
|
return;
|
}
|
break;
|
case "一层":
|
if (buttonElement) {
|
heightNum.value = 1;
|
heightNum1.value = heightNum.value + 14;
|
buttonElement.classList.toggle("active", tiles3dLayer.show);
|
}
|
break;
|
case "八层":
|
if (buttonElement) {
|
heightNum.value = 15;
|
heightNum1.value = heightNum.value + 15;
|
buttonElement.classList.toggle("active", tiles3dLayer.show);
|
}
|
break;
|
}
|
tiles3dLayer.style = new Cesium.Cesium3DTileStyle({
|
color: {
|
conditions: [
|
["${标高} > " + heightNum.value + " && ${标高} < " + heightNum1.value + " ", "rgba(255, 255, 255,1)"],
|
["true", "rgba(255, 255, 255,0)"]
|
]
|
}
|
});
|
};
|
// 返回初始位置
|
const flyHome = () => {
|
if (map.value) {
|
map.value.flyHome();
|
}
|
};
|
|
// 切换2D/3D视图
|
const toggleView = () => {
|
if (map.value) {
|
if (map.value.scene.mode === mars3d.Cesium.SceneMode.SCENE2D) {
|
map.value.scene.morphTo3D(0);
|
} else {
|
map.value.scene.morphTo2D(0);
|
}
|
}
|
};
|
|
// 暴露方法给父组件
|
defineExpose({
|
flyHome,
|
mapInstance: map, // 如果需要暴露实例
|
toggleView
|
// 其他需要暴露的方法...
|
});
|
|
onMounted(() => {
|
initMap();
|
});
|
|
onBeforeUnmount(() => {
|
// 清理地图资源
|
if (map.value) {
|
map.value.destroy();
|
}
|
});
|
|
// 监听props变化
|
watch(
|
[() => props.showfence, () => props.showanchor, () => props.showlayer, () => props.showthreeMap, () => props.mapcolor],
|
([newShowFence, newShowAnhocr, newShowLayer, newShowThreeMap], [oldShowFence, oldAnchor, oldLayer, oldThreeMap]) => {
|
if (newShowFence !== oldShowFence) {
|
if (newShowFence) {
|
//处理 围栏显示
|
AddFence();
|
} else {
|
graphicLayerFence.clear();
|
}
|
}
|
if (newShowAnhocr !== oldAnchor) {
|
if (newShowAnhocr) {
|
// 处理 基站显示
|
AddAnchor();
|
} else {
|
graphicLayerAnchor.clear();
|
}
|
}
|
if (newShowLayer !== oldLayer) {
|
if (newShowLayer) {
|
// 处理 本地瓦片
|
Addlayer();
|
} else {
|
xyztileLayer.remove();
|
}
|
}
|
if (newShowThreeMap !== oldThreeMap) {
|
if (newShowThreeMap) {
|
// 处理 三维地图
|
AddThreeMap();
|
} else {
|
tiles3dLayer.remove();
|
}
|
}
|
}
|
);
|
|
// 其他watch监听...
|
</script>
|
|
<style scoped>
|
.mars3d-container {
|
position: absolute;
|
top: 0;
|
left: 0;
|
width: 100%;
|
height: 100%;
|
}
|
</style>
|