人员路径和绘制电子围栏

pull/1/head
fangjiakai 2024-02-26 08:47:12 +08:00
parent 626c1a4bb9
commit 97b43e070f
18 changed files with 2258 additions and 114 deletions

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,79 @@
<template>
<div id="bi_container">
<div id="map" class="map_bg"></div>
<el-button type="primary" @click="confrim"></el-button>
<el-button type="primary" @click="clear"></el-button>
<el-button type="primary" @click="reduction"></el-button>
<span>当前选中楼层id:{{ model_id }}</span>
</div>
</template>
<script setup>
import { useVModels } from "@vueuse/core";
import { onBeforeUnmount, onMounted, ref } from "vue";
import {
clearEnclosure,
handleEnclosure,
handleMouseClick,
initMap,
reduction,
showEnclosure,
} from "./map";
import { useUserStore } from "@/pinia/user.js";
import { getEnterpriseInfo } from "@/request/enterprise_management.js";
const props = defineProps({
positions: {
type: Array,
required: true,
default: () => [],
},
modUuid: {
type: String,
required: true,
default: "",
},
});
const userStore = useUserStore();
const CORPINFO_ID = userStore.getUserInfo.CORPINFO_ID;
const data = [];
const model_id = ref(0);
const emits = defineEmits(["update:positions", "update:modUuid"]);
const { positions, modUuid } = useVModels(props, emits);
const confrim = () => {
showEnclosure(data);
positions.value = [...data];
modUuid.value = model_id.value;
};
const clear = () => {
clearEnclosure(data);
data.length = 0;
positions.value = [];
};
onMounted(async () => {
const corp = await getEnterpriseInfo({ CORPINFO_ID });
initMap(corp.pd);
handleMouseClick(model_id);
handleEnclosure(data);
});
onBeforeUnmount(() => {
window.$scene = null;
window.$icy = null;
window.$carmer = null;
});
</script>
<style scoped lang="scss">
#bi_container {
width: 100%;
height: 500px;
color: #ffffff;
position: relative;
font-size: 14px;
.map_bg {
width: 100%;
height: 100%;
}
}
</style>

View File

@ -0,0 +1,167 @@
import axios from "axios";
import { useUserStore } from "@/pinia/user";
import pinia from "@/pinia";
const userStore = useUserStore(pinia);
const pls_ip = userStore.getUserInfo.POST_URL;
const url = pls_ip.replace("8084", "9000") + "/buildr/public/models/glb/";
export class Loadglb {
static modelMap = {};
constructor(icy) {
this.icy = icy;
this.model_def_group = null;
this.scene = null;
this.idList = [];
this.entityList = [];
this.number = 0;
this.building_group = null;
this.groundHeight = 0.2; // 设置地面的高度
}
async fetchData() {
try {
const response = await axios.get(
pls_ip.replace("8084", "9000") +
"/buildr/public/models/scene_000001.json"
);
// 将获取的数据保存到实例的属性中
this.scene = response.data.scene;
this.model_def_group = response.data.scene.model_def_group;
const map = new Map();
this.model_def_group.forEach((i) => {
map.set(i.guid.split("-")[3], { name: i.name, id: i.guid });
});
this.scene.building_group.forEach((i) => {
i.layer_group.forEach((a) => {
a.modelName = map.get(a.guid[0].split("-")[3]);
});
});
this.building_group = this.scene.building_group;
// console.log(this.building_group);
} catch (error) {
// 处理可能错误
console.error(error);
}
}
async model(Model) {
try {
// 执行 axios 请求并等待 fetchData 函数的异步操作完成
await this.fetchData();
const modelList = {};
this.scene.model_def_group.forEach((item) => {
modelList[item.guid] = item.name;
});
const modelTotal = this.scene.building_group.length;
this.createModel(Model, modelList, modelTotal);
this.scene.ground_group.forEach((ground) => {
const lon = ground.transform.translate.x;
const lat = ground.transform.translate.y;
const scale = ground.transform.scale;
ground.model_ref_group.forEach((layer) => {
// console.log(layer,'111');
const height = this.groundHeight;
const unit = {
lon,
lat,
scale,
id: layer.model_def_guid,
};
this.logNum([layer], height, unit, modelList, Model);
});
});
} catch (error) {
// 处理可能的错误
console.error(error);
}
}
createModel(Model, modelList, modelTotal) {
this.scene.building_group.forEach((building, index) => {
if (
Number(this.number * 2) <= index &&
index < Number((this.number + 1) * 2)
) {
const lon = building.transform.translate.x;
const lat = building.transform.translate.y;
const scale = building.transform.scale;
building.layer_group.forEach((layer) => {
// console.log(layer,'111');
const height = Number(layer.height[0] + this.groundHeight);
const unit = {
lon,
lat,
scale,
id: layer.guid[0],
};
this.logNum(layer.model_ref_group, height, unit, modelList, Model);
});
}
});
if (this.number * 2 < modelTotal) {
const throttle = setTimeout(() => {
this.number++;
this.createModel(Model, modelList, modelTotal);
sessionStorage.setItem("loading", (this.number * 2) / modelTotal);
clearTimeout(throttle);
}, 20);
if (
(this.number * 2) / modelTotal >= 0.5 &&
(this.number * 2) / modelTotal < 0.8
) {
sessionStorage.setItem("loadModel", "过半");
} else if ((this.number * 2) / modelTotal >= 0.8) {
sessionStorage.setItem("loadModel", "临底");
}
} else {
console.log(
"加载完成 ,此次加载:" +
modelTotal +
"栋建筑,共:" +
this.model_def_group.length +
"个模型"
);
sessionStorage.setItem("loadModel", "完成");
}
}
logNum(red, height, unit, modelList, Model) {
red.forEach((item) => {
if (this.idList.indexOf(item.model_def_guid) !== -1) {
return;
}
this.idList.push(item.model_def_guid);
if (
Object.prototype.hasOwnProperty.call(
Loadglb.modelMap,
`${unit.id.split("-")[3].slice(0, unit.id.split("-")[3].length - 2)}`
)
) {
Loadglb.modelMap[
`${unit.id.split("-")[3].slice(0, unit.id.split("-")[3].length - 2)}`
].push(item.model_def_guid);
} else {
Loadglb.modelMap[
`${unit.id.split("-")[3].slice(0, unit.id.split("-")[3].length - 2)}`
] = [item.model_def_guid];
}
const m = new Model(this.icy, {
id: item.model_def_guid,
name: "建筑",
url: `${url}${modelList[item.model_def_guid]}.gltf`,
height,
lon: unit.lon,
lat: unit.lat,
scale: unit.scale,
angle: [90, 0, 0],
// angle: [97.4843053, 0, 0]
});
this.entityList.push(m);
});
}
getArr() {
return this.building_group;
}
}

View File

@ -0,0 +1,228 @@
import { Loadglb } from "./loadglb.js";
const loadMap = 3;
let $entityTransparent = [];
const clickModel = new Map();
let enclosure = null;
export const initMap = (corp) => {
window.$scene = new window.CustomCesium.Scene(
"map",
corp.CORP_NAME,
Number(loadMap)
);
window.$icy = window.$scene.getIcy();
window.$carmer = new window.CustomCesium.Carmer(window.$icy);
window.$icy.viewer.targetFrameRate = "30";
window.$icy.viewer.scene.light = new window.Cesium.DirectionalLight({
// 去除时间原因影响模型颜色
direction: new window.Cesium.Cartesian3(
0.35492591601301104,
-0.8909182691839401,
-0.2833588392420772
),
});
const [wgsLat, wgsLon] = bd09ToWgs84(corp.LATITUDE, corp.LONGITUDE);
console.log(corp.LATITUDE, corp.LONGITUDE);
flyTo(wgsLon, wgsLat);
// 亮度设置
const stages = window.$icy.viewer.scene.postProcessStages;
window.$icy.viewer.scene.brightness =
window.$icy.viewer.scene.brightness ||
stages.add(window.Cesium.PostProcessStageLibrary.createBrightnessStage());
window.$icy.viewer.scene.brightness.enabled = true;
window.$icy.viewer.scene.brightness.uniforms.brightness = Number(1.05); // 此处亮度值为倍数
createGlb(); // 加载glb
};
const bd09ToWgs84 = (bdLat, bdLon) => {
const pi = Math.PI;
const x_pi = (pi * 3000.0) / 180.0;
const x = bdLon - 0.0065;
const y = bdLat - 0.006;
const z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * x_pi);
const theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * x_pi);
const wgsLon = z * Math.cos(theta);
const wgsLat = z * Math.sin(theta);
return [wgsLat, wgsLon];
};
// eslint-disable-next-line no-unused-vars
const flyTo = (lng, lat) => {
window.$carmer.flyTo({
// 视角飞入
maxHeight: 1500,
time: 1,
position: [lng, lat, 200],
angle: [0, -60, 0],
});
};
const createGlb = () => {
// 加载glb建筑模型
const loadGlb = new Loadglb(window.$icy);
loadGlb.model(window.CustomCesium.Model);
};
// eslint-disable-next-line no-unused-vars
export const handleMouseClick = (model_id) => {
// 加载鼠标拾取
const $mouse = new window.CustomCesium.Mouse(window.$icy);
// 隐藏逻辑
$mouse.mouseLeft((model) => {
if (model._name === "建筑") {
model_id.value = model._id;
console.log(model_id);
clickBuilding(model);
}
});
};
const clickBuilding = (model) => {
if (
Loadglb.modelMap[
`${model._id.split("-")[3].slice(0, model._id.split("-")[3].length - 2)}`
]
) {
if (
clickModel.get(
`${model._id
.split("-")[3]
.slice(0, model._id.split("-")[3].length - 2)}`
) === undefined
) {
// 如果这个模型是第一次点击
const m =
Loadglb.modelMap[
`${model._id
.split("-")[3]
.slice(0, model._id.split("-")[3].length - 2)}`
]; // 整栋楼的id集合
// console.log(m, '整栋楼id');
if (m.length > 1) {
// 当前点击这建筑是否有多层
m.forEach((id) => {
const entity = window.$icy.viewer.entities.getById(id);
if (model.id !== id) {
// 排除当前点击楼层
if (entity._icy.height > model._icy.height) {
if (
$entityTransparent.map((a) => a._id).indexOf(entity._id) === -1
) {
$entityTransparent.push(entity);
}
// console.log(this.$entityTransparent, '被隐藏的模型楼层集合');
entity.model.show = false;
}
} else {
clickModel.set(
`${model._id
.split("-")[3]
.slice(0, model._id.split("-")[3].length - 2)}`,
model._icy.height
); // 存储这个模型数据
}
});
}
} else {
// 如果点击过这个模型
const m =
Loadglb.modelMap[
`${model._id
.split("-")[3]
.slice(0, model._id.split("-")[3].length - 2)}`
]; // 整栋楼的id集合
// console.log(m, '整栋楼id');
if (m.length > 1) {
// 当前点击这建筑是否有多层
if (
model._icy.height ===
clickModel.get(
`${model._id
.split("-")[3]
.slice(0, model._id.split("-")[3].length - 2)}`
)
) {
m.forEach((id) => {
const entity = window.$icy.viewer.entities.getById(id);
entity.model.show = true;
clickModel.delete(
`${model._id
.split("-")[3]
.slice(0, model._id.split("-")[3].length - 2)}`
);
});
} else {
m.forEach((id) => {
const entity = window.$icy.viewer.entities.getById(id);
if (model.id !== id) {
// 排除当前点击楼层
if (entity._icy.height > model._icy.height) {
if (
$entityTransparent.map((a) => a._id).indexOf(entity._id) ===
-1
) {
$entityTransparent.push(entity);
}
// console.log(this.$entityTransparent, '被隐藏的模型楼层集合');
entity.model.show = false;
}
} else {
clickModel.set(
`${model._id
.split("-")[3]
.slice(0, model._id.split("-")[3].length - 2)}`,
model._icy.height
); // 存储这个模型数据
}
});
}
}
}
// model.model.color = new Cesium.Color(1, 1, 1, 0.6);
}
};
// 还原建筑物
export const reduction = () => {
// 揭盖一键还原
if ($entityTransparent.length !== 0) {
$entityTransparent = $entityTransparent.forEach((item) => {
item.model.color = new window.Cesium.Color(1, 1, 1, 1);
item.model.show = true;
});
$entityTransparent = [];
}
};
export const handleEnclosure = (positions) => {
const $mouse = new window.CustomCesium.Mouse(window.$icy);
enclosure = new window.CustomCesium.Enclosure(window.$icy);
// 编辑围栏使用鼠标控件参照demo.html
enclosure.start();
$mouse.mouseRight((e) => {
enclosure.add(e.lng, e.lat, e.alt);
positions.push([e.lng, e.lat, e.alt]);
});
};
export const clearEnclosure = () => {
if (enclosure) {
try {
enclosure.show(false);
} catch (e) {}
enclosure.clear();
enclosure = new window.CustomCesium.Enclosure(window.$icy);
enclosure.start();
}
};
export const showEnclosure = (positions) => {
enclosure.finish();
enclosure.showDataSource(
positions, // 数据
30, // 高度
"yellow" // 颜色默认黄色
);
enclosure.show(true);
};

View File

@ -2,7 +2,11 @@ import { post } from "./axios";
export const getRealTimeList = (params) => post("/map/getRealTimeList", params); // 获取在线人员列表 export const getRealTimeList = (params) => post("/map/getRealTimeList", params); // 获取在线人员列表
export const getFenceList = (params) => post("/map/getFenceList", params); // 获取围栏列表 export const getFenceList = (params) => post("/map/getFenceList", params); // 获取围栏列表
export const getPersonnelTrajectories = (params) =>
post("/map/getPersonnelTrajectories", params); // 获取拥有历史轨迹的全部角色
export const getCharacterTrajectories = (params) =>
post("/map/getCharacterTrajectories", params); // 获取轨迹
export const getUserByCardNo = (params) => export const getUserByCardNo = (params) =>
post("/user/getUserByCardNo", params); post("/user/getUserByCardNo", params);

View File

@ -23,6 +23,8 @@ export const setAssignmentTicketAreaSettingsDeactivateOrEnable = (params) =>
post("/positAlarm/otherRegion/editStatus", params); // 作业票区域设置停用启用 post("/positAlarm/otherRegion/editStatus", params); // 作业票区域设置停用启用
export const setAssignmentTicketAreaSettingsDelete = (params) => export const setAssignmentTicketAreaSettingsDelete = (params) =>
post("/positAlarm/otherRegion/regionDelete", params); // 作业票区域设置删除 post("/positAlarm/otherRegion/regionDelete", params); // 作业票区域设置删除
export const setPosition = (params) =>
post("/positAlarm/otherRegion/redrawTheArea", params); // 区域选点
// TODO 接口不对 // TODO 接口不对
export const getPathPlanningList = (params) => export const getPathPlanningList = (params) =>
post("/positAlarm/coordinateLine/addCoordinateLine", params); // 路径规划列表 post("/positAlarm/coordinateLine/addCoordinateLine", params); // 路径规划列表

View File

@ -17,5 +17,4 @@ export const setUnbindingDeployCardData = (params) =>
export const getNotBindTheCardData = (params) => export const getNotBindTheCardData = (params) =>
post("/deploy/card/thePersonWhoDidNotBindTheCard", params); // 批量解绑 post("/deploy/card/thePersonWhoDidNotBindTheCard", params); // 批量解绑
// 设备管理 ==== // 设备管理 ====

View File

@ -102,17 +102,17 @@ const bottomOptionsList = [
check: false, check: false,
action: handleTrajectory, action: handleTrajectory,
}, },
{ // {
img: new URL("/src/assets/images/map/bottom/ico2.png", import.meta.url) // img: new URL("/src/assets/images/map/bottom/ico2.png", import.meta.url)
.href, // .href,
imgSelect: new URL( // imgSelect: new URL(
"/src/assets/images/map/bottom/ico2_on.png", // "/src/assets/images/map/bottom/ico2_on.png",
import.meta.url // import.meta.url
).href, // ).href,
title: "视频", // title: "",
type: "video", // type: "video",
check: false, // check: false,
}, // },
{ {
img: new URL("/src/assets/images/map/bottom/ico3.png", import.meta.url) img: new URL("/src/assets/images/map/bottom/ico3.png", import.meta.url)
.href, .href,

View File

@ -11,7 +11,12 @@
<el-col :span="6"> <el-col :span="6">
<el-form-item prop="personnel"> <el-form-item prop="personnel">
<el-select v-model="data.searchForm.personnel" filterable> <el-select v-model="data.searchForm.personnel" filterable>
<el-option label="河北秦安" value="河北秦安" /> <el-option
v-for="item in data.personList"
:key="item.id"
:label="item.name ? `${item.name}(${item.id})` : item.id"
:value="item.id"
/>
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
@ -46,9 +51,6 @@
<el-slider v-model="currentSchedule" :max="data.maxSchedule" /> <el-slider v-model="currentSchedule" :max="data.maxSchedule" />
<div> <div>
<span class="time">{{ data.time }}</span> <span class="time">{{ data.time }}</span>
<span class="text-yellow ml-10">
当前所在图层<el-tag size="small">{{ data.position }}</el-tag>
</span>
</div> </div>
</div> </div>
<div class="speed ml-20"> <div class="speed ml-20">
@ -68,10 +70,21 @@
</template> </template>
<script setup> <script setup>
import { reactive } from "vue"; import { reactive, onBeforeUnmount } from "vue";
import { ElMessage } from "element-plus"; import { ElMessage } from "element-plus";
import { VideoPause, VideoPlay } from "@element-plus/icons-vue"; import { VideoPause, VideoPlay } from "@element-plus/icons-vue";
import {
getPersonnelTrajectories,
getCharacterTrajectories,
} from "@/request/map.js";
import dayjs from "dayjs";
import { useInterval } from "@vueuse/core"; import { useInterval } from "@vueuse/core";
import {
addPerson,
hadleDestroy,
peoMovement,
showLine,
} from "@/views/BI/js/history_trajectory.js";
const data = reactive({ const data = reactive({
searchForm: { searchForm: {
@ -79,21 +92,62 @@ const data = reactive({
dates: [], dates: [],
}, },
isPlay: false, isPlay: false,
maxSchedule: 100, maxSchedule: 0,
time: "2021-08-01 12:00:00", time: "0000-00-00 00:00:00",
position: "图层1", position: "图层1",
speed: 1.0, speed: 1.0,
personList: [],
positions: [],
}); });
const { const {
counter: currentSchedule, counter: currentSchedule,
pause, pause,
resume, resume,
reset,
} = useInterval(() => 1000 / data.speed, { } = useInterval(() => 1000 / data.speed, {
immediate: false, immediate: false,
controls: true, controls: true,
callback: () => {}, callback: (count) => {
if (count >= data.maxSchedule) {
data.isPlay = false;
pause();
reset();
}
const currentData = data.positions[count].split(",");
const previousValue = count === 0 ? undefined : data.positions[count - 1];
const previousPosition = previousValue ? previousValue.split(",") : [];
data.time = dayjs(Number(currentData[3])).format("YYYY-MM-DD HH:mm:ss");
peoMovement(
data.searchForm.personnel,
currentData[0],
currentData[1],
currentData[2],
previousValue
? angleMath(
previousPosition[0],
previousPosition[1],
currentData[0],
currentData[1]
)
: undefined
);
},
}); });
const fnGetData = () => {
const angleMath = (lon, lat, lon1, lat1) => {
//
const radian = Math.atan2(lon - lon1, lat - lat1); //
const angle = Number((180 / Math.PI) * radian - 180); //
return angle;
};
const getPersonList = async () => {
const res = await getPersonnelTrajectories();
data.personList = res.data.data;
};
getPersonList();
const fnGetData = async () => {
if (data.searchForm.personnel === "") { if (data.searchForm.personnel === "") {
ElMessage.warning("请选择人员"); ElMessage.warning("请选择人员");
return; return;
@ -102,8 +156,49 @@ const fnGetData = () => {
ElMessage.warning("请选择时间"); ElMessage.warning("请选择时间");
return; return;
} }
hadleDestroy();
data.positions = [];
pause();
reset();
await getTrajectoriesData(
data.searchForm.personnel,
data.searchForm.dates[0],
data.searchForm.dates[1],
1
);
data.maxSchedule = data.positions.length;
const firstData = data.positions[0].split(",");
data.time = dayjs(Number(firstData[3])).format("YYYY-MM-DD HH:mm:ss");
const person = data.personList.filter(
(item) => item.id === data.searchForm.personnel
)[0];
addPerson(
data.searchForm.personnel,
person.name ? person.name : person.id,
firstData[0],
firstData[1],
firstData[2]
);
const positions = data.positions.map((item) => {
const position = item.split(",");
return [position[0], position[1], Number(position[2]) + 0.5];
});
showLine(positions);
ElMessage.success("搜索成功"); ElMessage.success("搜索成功");
}; };
const getTrajectoriesData = async (id, startTime, endTime, type) => {
const res = await getCharacterTrajectories({
id,
startTime,
endTime,
type,
});
data.positions = data.positions.concat(res.data.data);
if (res.data.msg !== "人物轨迹结束") {
type++;
await getTrajectoriesData(id, startTime, endTime, type);
}
};
const fnPlay = () => { const fnPlay = () => {
data.isPlay = true; data.isPlay = true;
resume(); resume();
@ -112,6 +207,11 @@ const fnPause = () => {
data.isPlay = false; data.isPlay = false;
pause(); pause();
}; };
onBeforeUnmount(() => {
console.log("onBeforeUnmount");
hadleDestroy();
});
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View File

@ -55,6 +55,8 @@ import BottomOptions from "./components/bottom_options.vue";
import HistoricalTrajectoryOptions from "./components/historical_trajectory_options.vue"; import HistoricalTrajectoryOptions from "./components/historical_trajectory_options.vue";
import { useRouter } from "vue-router"; import { useRouter } from "vue-router";
import { initMap } from "./js/map"; import { initMap } from "./js/map";
import { useUserStore } from "@/pinia/user.js";
import { getEnterpriseInfo } from "@/request/enterprise_management.js";
const router = useRouter(); const router = useRouter();
const right_option = ref(true); const right_option = ref(true);
@ -62,9 +64,10 @@ const transitionKey = ref(0);
const leftCurrentComponent = ref(""); const leftCurrentComponent = ref("");
const rightCurrentComponent = ref(""); const rightCurrentComponent = ref("");
const isHistoricalTrajectory = ref(false); const isHistoricalTrajectory = ref(false);
const corp = { CORP_NAME: "河北秦安", lng: 119.44758654, lat: 39.91845908 }; const userStore = useUserStore();
const CORPINFO_ID = userStore.getUserInfo.CORPINFO_ID;
onMounted(() => { onMounted(async () => {
autofit.init({ autofit.init({
dh: document.querySelector(".map_bg").offsetHeight, dh: document.querySelector(".map_bg").offsetHeight,
dw: 1920, dw: 1920,
@ -72,9 +75,13 @@ onMounted(() => {
resize: true, resize: true,
}); });
transitionKey.value = Math.random(); transitionKey.value = Math.random();
initMap(corp); const corp = await getEnterpriseInfo({ CORPINFO_ID });
initMap(corp.pd);
}); });
onBeforeUnmount(() => { onBeforeUnmount(() => {
window.$scene = null;
window.$icy = null;
window.$carmer = null;
autofit.off(); autofit.off();
}); });
</script> </script>

View File

@ -0,0 +1,133 @@
let entityArr = [];
let billboardArr = [];
let line = [];
export const addPerson = (id, name, lon, lat, height) => {
entityArr = new window.CustomCesium.GroupModel("人物模型");
billboardArr = new window.CustomCesium.GroupModel("人物姓名");
addEntity(id, name, lon, lat, height);
addBillboard(id, name, lon, lat, height);
};
const addEntity = (id, name, lon, lat, height) => {
const obj = new window.CustomCesium.Model(window.$icy, {
url: "/src/assets/glb/person_000002_blue.gltf",
height: 0,
scale: 1,
angle: [-90, 0, 0],
});
let e;
// eslint-disable-next-line prefer-const
e = obj.clone(e);
const enModule = new window.CustomCesium.Model(
window.$icy,
{
name: "人物",
height,
id,
lon,
lat, // 位置
scale: 1, // 大小
angle: [-90, 0, 0],
},
e
);
// 修改模型方向 第一个参数是航向角
// enModule.updateAngle([-90,20,20]);
entityArr.add(enModule);
};
const addBillboard = (id, name, lon, lat, height) => {
// 创建广告牌
const bulletinBoard = new window.CustomCesium.BulletinBoard(window.$icy);
bulletinBoard.loadCanvas(drawCanvas(name), {
name: "人物铭牌",
id: id + "_name",
lon,
lat,
height: Number(height) + 2.05,
});
billboardArr.add(bulletinBoard);
};
// eslint-disable-next-line no-unused-vars
const drawCanvas = (name) => {
const canvas = document.createElement("canvas");
const width = 70;
const height = 20;
canvas.width = width;
canvas.height = height;
const borderRadius = 5; // 圆角半径
const ctx = canvas.getContext("2d");
ctx.beginPath();
ctx.moveTo(borderRadius, 0);
ctx.lineTo(width - borderRadius, 0);
ctx.arcTo(width, 0, width, borderRadius, borderRadius);
ctx.lineTo(width, height - borderRadius);
ctx.arcTo(width, height, width - borderRadius, height, borderRadius);
ctx.lineTo(borderRadius, height);
ctx.arcTo(0, height, 0, height - borderRadius, borderRadius);
ctx.lineTo(0, borderRadius);
ctx.arcTo(0, 0, borderRadius, 0, borderRadius);
ctx.closePath();
ctx.fillStyle = "rgba(42, 86, 158, 1)";
ctx.fill();
ctx.fillStyle = "#fff";
ctx.font = "normal 12px 'Microsoft YaHei'";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillText(name, width / 2, height / 2);
return canvas;
};
/**
* 显示轨迹线
*/
export const showLine = (positions) => {
line = new window.CustomCesium.Line(window.$icy, {
width: 10,
positions,
color: "#00ff00",
});
line.show(true);
// line.destroy();
};
/**
* id, lon, lat, height,alt 接收人物移动数据
* entityArr 接收人物模型分组
* billboardArr 接收人物铭牌分组
*/
export const peoMovement = (id, lon, lat, height, alt) => {
entityArr.children.forEach((b, m) => {
if (b.entity._id === id) {
b.updateAngle([alt, 0, 0]); // 改变人物朝向
b.animationMove(lon, lat, height, 1000, () => {}); // 人物平滑移动
billboardArr.children[m].animationMove(
lon,
lat,
Number(Number(height) + 2.05),
1000
); // 人物铭牌平滑移动
} else {
console.log(`人物移动数据错误`);
}
});
};
export const hadleDestroy = () => {
if (entityArr && entityArr.children) {
entityArr.children.forEach((e) => {
e.destroy();
});
billboardArr.children.forEach((e) => {
e.destroy();
});
line.destroy();
entityArr = [];
billboardArr = [];
line = [];
}
};

View File

@ -35,7 +35,8 @@ export const initMap = (corp) => {
-0.2833588392420772 -0.2833588392420772
), ),
}); });
flyTo(corp.lng, corp.lat); const [wgsLat, wgsLon] = bd09ToWgs84(corp.LATITUDE, corp.LONGITUDE);
flyTo(wgsLon, wgsLat);
// 亮度设置 // 亮度设置
const stages = window.$icy.viewer.scene.postProcessStages; const stages = window.$icy.viewer.scene.postProcessStages;
window.$icy.viewer.scene.brightness = window.$icy.viewer.scene.brightness =
@ -47,6 +48,18 @@ export const initMap = (corp) => {
handleMouseClick(); // 加载鼠标拾取 handleMouseClick(); // 加载鼠标拾取
}; };
const bd09ToWgs84 = (bdLat, bdLon) => {
const pi = Math.PI;
const x_pi = (pi * 3000.0) / 180.0;
const x = bdLon - 0.0065;
const y = bdLat - 0.006;
const z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * x_pi);
const theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * x_pi);
const wgsLon = z * Math.cos(theta);
const wgsLat = z * Math.sin(theta);
return [wgsLat, wgsLon];
};
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
const flyTo = (lng, lat) => { const flyTo = (lng, lat) => {
window.$carmer.flyTo({ window.$carmer.flyTo({

View File

@ -2,7 +2,7 @@ import pako from "pako"; // 解密gzip插件
import { useWebSocket } from "@vueuse/core"; import { useWebSocket } from "@vueuse/core";
import { useUserStore } from "@/pinia/user"; import { useUserStore } from "@/pinia/user";
import pinia from "@/pinia"; import pinia from "@/pinia";
import { getRealTimeList, getFenceList } from "@/request/map"; import { getFenceList } from "@/request/map";
const userStore = useUserStore(pinia); const userStore = useUserStore(pinia);
const pls_ip = userStore.getUserInfo.POST_URL; const pls_ip = userStore.getUserInfo.POST_URL;
@ -39,50 +39,20 @@ const { open, close } = useWebSocket(
billboardArr.children.forEach((e) => { billboardArr.children.forEach((e) => {
e.destroy(); e.destroy();
}); });
entityArr = [];
billboardArr = [];
} }
}, },
} }
); );
export const handleTrajectory = (b) => { export const handleTrajectory = (b) => {
if (b) { if (b) {
getOnlineUser();
open(); open();
} else { } else {
close(); close();
} }
}; };
const getOnlineUser = async () => {
entityArr = new window.CustomCesium.GroupModel("人物模型");
billboardArr = new window.CustomCesium.GroupModel("人物姓名");
// await getRealTimeList();
const { data } = await getRealTimeList();
let userList = data.data;
userList = [
{
cardNo: "8379",
name: "大大",
longitude: 119.44768654,
latitude: 39.91745908,
altitude: 0,
},
{
cardNo: "9979",
name: "小小小小",
longitude: 119.44758754,
latitude: 39.91745908,
altitude: 0,
},
];
userList.forEach(({ cardNo, name, longitude, latitude, altitude }) => {
addEntity(cardNo, name, longitude, latitude, altitude);
addBillboard(cardNo, name, longitude, latitude, altitude + 2);
});
entityArr.show(true);
billboardArr.show(true);
};
const addEntity = (id, name, lon, lat, height) => { const addEntity = (id, name, lon, lat, height) => {
const obj = new window.CustomCesium.Model(window.$icy, { const obj = new window.CustomCesium.Model(window.$icy, {
url: "/src/assets/glb/person_000002_blue.gltf", url: "/src/assets/glb/person_000002_blue.gltf",
@ -119,7 +89,7 @@ const addBillboard = (id, name, lon, lat, height) => {
id: id + "_name", id: id + "_name",
lon, lon,
lat, lat,
height, height: height + 2.05,
}); });
billboardArr.add(bulletinBoard); billboardArr.add(bulletinBoard);
}; };
@ -190,31 +160,49 @@ function peoMovement(moveData) {
const map = new Map(); const map = new Map();
moveData.forEach((i, n) => { moveData.forEach((i, n) => {
map.set(i.split(",")[0], n); map.set(i.split(",")[0], n);
}); if (!entityArr || !entityArr.children) {
entityArr.children.forEach((b, m) => { entityArr = new window.CustomCesium.GroupModel("人物模型");
const index = map.get(b.entity._id); billboardArr = new window.CustomCesium.GroupModel("人物姓名");
const item = moveData[index]; const entity = i.split(",");
if (index !== undefined) { addEntity(entity[0], entity[8], entity[1], entity[2], entity[4]);
const itData = item.split(","); // itData下标0=【卡号】1,24=【lonlatalt】3=【人物朝向角度】5=【工号】6=【当前楼层】 addBillboard(entity[0], entity[8], entity[1], entity[2], entity[4]);
if (itData[4] != null && itData.length >= 6) {
b.updateAngle([itData[3], 0, 0]); // 改变人物朝向
b.animationMove(
itData[1],
itData[2],
Number(itData[4]),
2000,
() => {}
); // 人物平滑移动
billboardArr.children[m].animationMove(
itData[1],
itData[2],
Number(Number(itData[4]) + 2.05),
2000
); // 人物铭牌平滑移动
} else {
console.log(`人物移动数据错误,错误参数:'${item}'`);
}
} }
entityArr.children.forEach((b, m) => {
if (!b.entity) {
const entity = i.split(",");
addEntity(entity[0], entity[8], entity[1], entity[2], entity[4]);
addBillboard(entity[0], entity[8], entity[1], entity[2], entity[4]);
return;
}
const index = map.get(b.entity._id);
const item = moveData[index];
if (index !== undefined) {
const itData = item.split(","); // itData下标0=【卡号】1,24=【lonlatalt】3=【人物朝向角度】5=【工号】6=【当前楼层】
if (itData[4] != null && itData.length >= 6) {
b.updateAngle([itData[3], 0, 0]); // 改变人物朝向
b.animationMove(
itData[1],
itData[2],
Number(itData[4]),
2000,
() => {}
); // 人物平滑移动
billboardArr.children[m].animationMove(
itData[1],
itData[2],
Number(Number(itData[4]) + 2.05),
2000
); // 人物铭牌平滑移动
} else {
console.log(`人物移动数据错误,错误参数:'${item}'`);
}
} else {
const entity = i.split(",");
addEntity(entity[0], entity[8], entity[1], entity[2], entity[4]);
addBillboard(entity[0], entity[8], entity[1], entity[2], entity[4]);
}
});
}); });
} }

View File

@ -13,12 +13,7 @@
</div> </div>
<div class="video"> <div class="video">
<ali-player <ali-player
v-if="tabsIndex === 0 && ptVideoSrc" v-if="tabsIndex === 0 && cpVideoSrc"
:source="VITE_FILE_URL + ptVideoSrc"
height="182px"
/>
<ali-player
v-if="tabsIndex === 1 && cpVideoSrc"
:source="VITE_FILE_URL + cpVideoSrc" :source="VITE_FILE_URL + cpVideoSrc"
height="182px" height="182px"
/> />
@ -32,7 +27,7 @@ import { getVideo } from "@/request/large_screen_data_display.js";
import AliPlayer from "@/components/ali-player/index.vue"; import AliPlayer from "@/components/ali-player/index.vue";
const VITE_FILE_URL = import.meta.env.VITE_FILE_URL; const VITE_FILE_URL = import.meta.env.VITE_FILE_URL;
const tabsList = ["平台视频", "企业视频"]; const tabsList = ["企业视频"];
const tabsIndex = ref(0); const tabsIndex = ref(0);
const ptVideoSrc = ref(""); const ptVideoSrc = ref("");
const cpVideoSrc = ref(""); const cpVideoSrc = ref("");

View File

@ -1,5 +1,6 @@
<template> <template>
<el-dialog v-model="visible" title="选点" :on-close="fnClose"> <el-dialog v-model="visible" title="选点" :on-close="fnClose">
<map-tools v-if="visible" ref="mapToolsRef" v-model:positions="positions" />
<template #footer> <template #footer>
<el-button @click="fnClose"></el-button> <el-button @click="fnClose"></el-button>
<el-button type="primary" @click="fnSubmit"></el-button> <el-button type="primary" @click="fnSubmit"></el-button>
@ -8,9 +9,12 @@
</template> </template>
<script setup> <script setup>
import { ref } from "vue";
import { useVModels } from "@vueuse/core"; import { useVModels } from "@vueuse/core";
import { debounce } from "throttle-debounce"; import { debounce } from "throttle-debounce";
import { ElMessage } from "element-plus"; import { ElMessage } from "element-plus";
import MapTools from "@/components/map_tools";
import { setPosition } from "@/request/map_settings.js";
const props = defineProps({ const props = defineProps({
visible: { visible: {
@ -18,15 +22,26 @@ const props = defineProps({
required: true, required: true,
default: false, default: false,
}, },
id: {
type: Number,
required: true,
default: 0,
},
}); });
const emits = defineEmits(["update:visible", "get-data"]); const emits = defineEmits(["update:visible", "get-data"]);
const { visible } = useVModels(props, emits); const { visible } = useVModels(props, emits);
const positions = ref([]);
const fnClose = () => { const fnClose = () => {
visible.value = false; visible.value = false;
}; };
const fnSubmit = debounce( const fnSubmit = debounce(
1000, 1000,
async () => { async () => {
if (positions.value.length < 3) {
ElMessage.warning("请选择至少3个点");
return;
}
await setPosition({ id: props.id, list: JSON.stringify(positions.value) });
ElMessage.success("保存成功"); ElMessage.success("保存成功");
fnClose(); fnClose();
emits("get-data"); emits("get-data");

View File

@ -104,6 +104,7 @@
@get-data="fnResetPagination" @get-data="fnResetPagination"
/> />
<selecting-points <selecting-points
:id="data.selectingPointsDialog.id"
v-model:visible="data.selectingPointsDialog.visible" v-model:visible="data.selectingPointsDialog.visible"
@get-data="fnResetPagination" @get-data="fnResetPagination"
/> />

View File

@ -59,24 +59,24 @@
<el-button type="primary" text link @click="fnQrCode(row)"> <el-button type="primary" text link @click="fnQrCode(row)">
二维码 二维码
</el-button> </el-button>
<el-button <!-- <el-button-->
v-if="buttonJurisdiction.edit" <!-- v-if="buttonJurisdiction.edit"-->
type="primary" <!-- type="primary"-->
text <!-- text-->
link <!-- link-->
@click=" <!-- @click="-->
router.push({ <!-- router.push({-->
path: '/risk_control/identifying_parts/resources_risk', <!-- path: '/risk_control/identifying_parts/resources_risk',-->
query: { <!-- query: {-->
IDENTIFICATIONPARTS_ID: row.IDENTIFICATIONPARTS_ID, <!-- IDENTIFICATIONPARTS_ID: row.IDENTIFICATIONPARTS_ID,-->
PARTSNAME: row.PARTSNAME, <!-- PARTSNAME: row.PARTSNAME,-->
RISKUNITNAME: row.RISKUNITNAME, <!-- RISKUNITNAME: row.RISKUNITNAME,-->
}, <!-- },-->
}) <!-- })-->
" <!-- "-->
> <!-- >-->
匹配资源存在风险 <!-- 匹配资源存在风险-->
</el-button> <!-- </el-button>-->
<el-button <el-button
v-if="buttonJurisdiction.edit" v-if="buttonJurisdiction.edit"
type="primary" type="primary"
@ -148,12 +148,11 @@ import { ElMessage, ElMessageBox } from "element-plus";
import { nextTick, reactive } from "vue"; import { nextTick, reactive } from "vue";
import LayoutImportFile from "@/components/import_file/index.vue"; import LayoutImportFile from "@/components/import_file/index.vue";
import Add from "./components/add.vue"; import Add from "./components/add.vue";
import { useRouter } from "vue-router"; // import { useRouter } from "vue-router";
import QrCode from "./components/qr_code.vue"; import QrCode from "./components/qr_code.vue";
import LayoutTooltipImg from "@/components/tooltip_img/index.vue"; import LayoutTooltipImg from "@/components/tooltip_img/index.vue";
import useButtonJurisdiction from "@/assets/js/useButtonJurisdiction.js"; import useButtonJurisdiction from "@/assets/js/useButtonJurisdiction.js";
// const router = useRouter();
const router = useRouter();
const { list, pagination, searchForm, fnGetData, fnResetPagination, tableRef } = const { list, pagination, searchForm, fnGetData, fnResetPagination, tableRef } =
useListData(getIdentifyingPartsList); useListData(getIdentifyingPartsList);
const data = reactive({ const data = reactive({

View File

@ -16,7 +16,7 @@
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="6"> <el-col :span="6">
<el-form-item label="风险分级"> <el-form-item label="风险分级" prop="LEVELID">
<el-select v-model="searchForm.LEVELID"> <el-select v-model="searchForm.LEVELID">
<el-option <el-option
v-for="item in riskClassificationList" v-for="item in riskClassificationList"
@ -43,7 +43,7 @@
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="6"> <el-col :span="6">
<el-form-item label="事故类型"> <el-form-item label="事故类型" prop="BIANMA">
<el-select v-model="searchForm.BIANMA"> <el-select v-model="searchForm.BIANMA">
<el-option <el-option
v-for="item in accidentTypeList" v-for="item in accidentTypeList"