537 lines
14 KiB
Vue
537 lines
14 KiB
Vue
<template>
|
||
<div class="main-content">
|
||
<div class="main-content-title">
|
||
<span>路线导航</span>
|
||
<el-icon size="16" color="#fff" @click="closeDiv"><Close /></el-icon>
|
||
</div>
|
||
<div class="item-classify">
|
||
<div class="item-classify-item">
|
||
<span>起点:</span>
|
||
<el-input :value="mapPosition.startPosition" disabled />
|
||
<el-button type="primary" @click="startPicking">图上选点</el-button>
|
||
</div>
|
||
<div class="item-classify-item">
|
||
<span>终点:</span>
|
||
<el-input :value="mapPosition.endPosition" disabled />
|
||
<el-button type="primary" @click="endPicking">图上选点</el-button>
|
||
</div>
|
||
</div>
|
||
<div class="table-list" v-if="tableData.length > 0">
|
||
<div class="table-head">
|
||
<div class="tr">
|
||
<div class="td"><span>序号</span></div>
|
||
<div class="td">推荐线路</div>
|
||
</div>
|
||
</div>
|
||
<div class="table-body">
|
||
<div class="tr" v-for="(item, index) in tableData" :key="index">
|
||
<div class="td">方案{{ index + 1 }}</div>
|
||
<div class="td">
|
||
<div>
|
||
<el-button type="primary" @click="highLightRoute(item)" size="small">高亮路线</el-button>
|
||
<el-button type="primary" @click="startFly(item, index)" size="small">开始漫游</el-button>
|
||
</div>
|
||
<div>{{ htmlDeal(item) }}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="speed-set" v-if="tableData.length > 0">
|
||
<span>速度:</span>
|
||
<el-input-number v-model="mapPosition.speed" controls-position="right" />
|
||
<span>(km/h)</span>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script lang="ts" setup>
|
||
//引入cesium基础库
|
||
import "mars3d-cesium/Build/Cesium/Widgets/widgets.css";
|
||
import * as Cesium from "mars3d-cesium";
|
||
//导入mars3d主库
|
||
import "mars3d/dist/mars3d.css";
|
||
import * as mars3d from "mars3d";
|
||
import { ref, watch, onMounted, onUnmounted, reactive, computed } from "vue";
|
||
import type { UnwrapRef } from "vue";
|
||
import { ElMessage } from "element-plus";
|
||
import { GlobalStore } from "@/stores";
|
||
const props = defineProps(["mapInstance"]);
|
||
const emits = defineEmits(["hiddenConfim"]);
|
||
const mapPosition = ref({
|
||
startPosition: "",
|
||
endPosition: "",
|
||
speed: 200
|
||
});
|
||
const globalStore = GlobalStore();
|
||
const tableData = ref<any>([]);
|
||
let map: any = props.mapInstance;
|
||
let routeLayer: any;
|
||
let gaodeRoute: any; // 高德路径规划
|
||
let startGraphic: any, endGraphic: any;
|
||
onMounted(() => {
|
||
routeLayer = new mars3d.layer.GraphicLayer();
|
||
map.addLayer(routeLayer);
|
||
gaodeRoute = new mars3d.query.GaodeRoute({
|
||
key: ["ad31e514e7e740179d6d8f182720bcf5"]
|
||
});
|
||
});
|
||
// 模型路线漫游
|
||
const startFly = (row: any, index: number) => {
|
||
// 加载完成在加载小车,否则地形未加载完成,小车会处于地下
|
||
productRoute(row, index);
|
||
};
|
||
// 生成路线
|
||
const productRoute = (obj: any, index: number) => {
|
||
console.log(obj);
|
||
console.log(startGraphic);
|
||
console.log(endGraphic);
|
||
const graphic = routeLayer.getGraphicById(obj.id);
|
||
console.log(graphic);
|
||
let pointsArr = [] as any;
|
||
obj.points.map((item: any) => {
|
||
pointsArr.push(mars3d.LngLatPoint.fromArray(item));
|
||
});
|
||
console.log(pointsArr);
|
||
const newRoutePath = new mars3d.graphic.FixedRoute({
|
||
id: obj.id + "-" + index,
|
||
name: "路线-" + index,
|
||
speed: 200,
|
||
positions: pointsArr,
|
||
clockLoop: false, // 是否循环播放
|
||
model: {
|
||
url: "//data.mars3d.cn/gltf/mars/jingche/jingche.gltf",
|
||
heading: 90,
|
||
mergeOrientation: true, // 用于设置模型不是标准的方向时的纠偏处理,在orientation基础的方式值上加上设置是heading值
|
||
minimumPixelSize: 50
|
||
},
|
||
camera: {
|
||
type: "gs",
|
||
pitch: -30,
|
||
radius: 500
|
||
},
|
||
polyline: {
|
||
color: "#ffff00",
|
||
width: 3
|
||
}
|
||
});
|
||
console.log(newRoutePath);
|
||
console.log(newRoutePath.info);
|
||
routeLayer.addGraphic(newRoutePath);
|
||
// 绑定popup
|
||
bindPopup(newRoutePath);
|
||
newRoutePath.autoSurfaceHeight().then(function (e: any) {
|
||
// 启动漫游
|
||
newRoutePath.flyTo();
|
||
newRoutePath.start();
|
||
|
||
addParticleSystem(newRoutePath.property);
|
||
});
|
||
};
|
||
const bindPopup = (fixedRoute: any) => {
|
||
fixedRoute.bindPopup(
|
||
`<div style="width: 200px">
|
||
<div>总 距 离:<span id="lblAllLen"> </span></div>
|
||
<div>总 时 间:<span id="lblAllTime"> </span></div>
|
||
<div>开始时间:<span id="lblStartTime"> </span></div>
|
||
<div>剩余时间:<span id="lblRemainTime"> </span></div>
|
||
<div>剩余距离:<span id="lblRemainLen"> </span></div>
|
||
</div>`,
|
||
{ closeOnClick: false }
|
||
);
|
||
|
||
// 刷新局部DOM,不影响popup面板的其他控件操作
|
||
fixedRoute.on(mars3d.EventType.postRender, function (event: any) {
|
||
const container = event.container; // popup对应的DOM
|
||
|
||
const params = fixedRoute?.info;
|
||
console.log(fixedRoute?.info);
|
||
if (!params) {
|
||
return;
|
||
}
|
||
|
||
const lblAllLen = container.querySelector("#lblAllLen");
|
||
if (lblAllLen) {
|
||
lblAllLen.innerHTML = mars3d.MeasureUtil.formatDistance(params.distance_all);
|
||
}
|
||
|
||
const lblAllTime = container.querySelector("#lblAllTime");
|
||
if (lblAllTime) {
|
||
lblAllTime.innerHTML = mars3d.Util.formatTime(params.second_all / map.clock.multiplier);
|
||
}
|
||
|
||
const lblStartTime = container.querySelector("#lblStartTime");
|
||
if (lblStartTime) {
|
||
lblStartTime.innerHTML = mars3d.Util.formatDate(Cesium.JulianDate.toDate(fixedRoute.startTime), "yyyy-M-d HH:mm:ss");
|
||
}
|
||
|
||
const lblRemainTime = container.querySelector("#lblRemainTime");
|
||
if (lblRemainTime) {
|
||
lblRemainTime.innerHTML = mars3d.Util.formatTime((params.second_all - params.second) / map.clock.multiplier);
|
||
}
|
||
|
||
const lblRemainLen = container.querySelector("#lblRemainLen");
|
||
if (lblRemainLen) {
|
||
lblRemainLen.innerHTML = mars3d.MeasureUtil.formatDistance(params.distance_all - params.distance) || "完成";
|
||
}
|
||
});
|
||
};
|
||
// 添加尾气粒子效果
|
||
function addParticleSystem(property: any) {
|
||
const particleSystem = new mars3d.graphic.ParticleSystem({
|
||
position: property,
|
||
style: {
|
||
image: "@/assets/images/Mars3DImg/particle/smoke.png",
|
||
particleSize: 12, // 粒子大小(单位:像素)
|
||
emissionRate: 20.0, // 发射速率 (单位:次/秒)
|
||
pitch: 40, // 俯仰角
|
||
maxHeight: 1000, // 超出该高度后不显示粒子效果
|
||
|
||
startColor: Cesium.Color.GREY.withAlpha(0.7), // 开始颜色
|
||
endColor: Cesium.Color.WHITE.withAlpha(0.0), // 结束颜色
|
||
startScale: 1.0, // 开始比例(单位:相对于imageSize大小的倍数)
|
||
endScale: 5.0, // 结束比例(单位:相对于imageSize大小的倍数)
|
||
minimumSpeed: 1.0, // 最小速度(米/秒)
|
||
maximumSpeed: 4.0 // 最大速度(米/秒)
|
||
},
|
||
attr: { remark: "车辆尾气" }
|
||
});
|
||
routeLayer.addGraphic(particleSystem);
|
||
}
|
||
let lastRoute: any;
|
||
// 高亮路线
|
||
const highLightRoute = (row: any) => {
|
||
const graphic = routeLayer.getGraphicById(row.id);
|
||
|
||
if (lastRoute) {
|
||
lastRoute.entityGraphic.material = lastRoute.entityGraphic.material_old;
|
||
lastRoute.entityGraphic.width = lastRoute.entityGraphic.width_old;
|
||
}
|
||
|
||
// 动画线材质
|
||
graphic.entityGraphic.width = 5;
|
||
graphic.entityGraphic.material = mars3d.MaterialUtil.createMaterialProperty(mars3d.MaterialType.LineFlow, {
|
||
color: Cesium.Color.CHARTREUSE,
|
||
image: "@/assets/images/Mars3DImg/textures/line-color-yellow.png",
|
||
speed: 20
|
||
});
|
||
|
||
map.flyToGraphic(graphic);
|
||
|
||
lastRoute = graphic;
|
||
};
|
||
// html格式处理
|
||
const htmlDeal = (row: any) => {
|
||
console.log(row);
|
||
let passArr = [...new Set(row.road)];
|
||
let html = `全场${mars3d.MeasureUtil.formatDistance(row.allDistance)},途径:${passArr.join(",")}`;
|
||
return html;
|
||
};
|
||
// 查询路线规划
|
||
const queryRoute = () => {
|
||
if (!startGraphic || !endGraphic) {
|
||
return;
|
||
}
|
||
routeLayer.clear();
|
||
console.log(startGraphic.coordinate);
|
||
console.log(endGraphic.coordinate);
|
||
tableData.value = [];
|
||
gaodeRoute.queryDriving({
|
||
points: [startGraphic.coordinate, endGraphic.coordinate],
|
||
strategy: 5,
|
||
success: (data: any) => {
|
||
console.log(data);
|
||
for (let i = 0; i < data.paths.length; i++) {
|
||
const firstItem = data.paths[i];
|
||
console.log(new Set(firstItem.road));
|
||
|
||
const points = firstItem.points;
|
||
if (!points || points.length < 1) {
|
||
continue;
|
||
}
|
||
|
||
const time = mars3d.Util.formatTime(firstItem.allDuration);
|
||
const distance = mars3d.MeasureUtil.formatDistance(firstItem.allDistance);
|
||
const html = "<div>总距离:" + distance + "<br/>所需时间:" + time + "</div>";
|
||
|
||
const graphic = new mars3d.graphic.PolylineEntity({
|
||
positions: points,
|
||
style: {
|
||
clampToGround: true,
|
||
material: Cesium.Color.fromRandom({
|
||
alpha: 0.7
|
||
}),
|
||
width: 5
|
||
},
|
||
attr: firstItem,
|
||
popup: html
|
||
});
|
||
routeLayer.addGraphic(graphic);
|
||
tableData.value.push({
|
||
id: graphic.id,
|
||
...data.paths[i]
|
||
});
|
||
}
|
||
// const firstItem = data.paths[0];
|
||
// const points = firstItem.points;
|
||
// if (!points || points.length < 1) {
|
||
// return;
|
||
// }
|
||
|
||
// const time = mars3d.Util.formatTime(firstItem.allDuration);
|
||
// const distance = mars3d.MeasureUtil.formatDistance(firstItem.allDistance);
|
||
// const html = "<div>总距离:" + distance + "<br/>所需时间:" + time + "</div>";
|
||
|
||
// const graphic = new mars3d.graphic.PolylineEntity({
|
||
// positions: points,
|
||
// style: {
|
||
// clampToGround: true,
|
||
// material: Cesium.Color.AQUA.withAlpha(0.8),
|
||
// width: 5
|
||
// },
|
||
// attr: firstItem,
|
||
// popup: html
|
||
// });
|
||
// routeLayer.addGraphic(graphic);
|
||
// const allTime = mars3d.Util.formatTime(firstItem.allDuration);
|
||
// const allDistance = mars3d.MeasureUtil.formatDistance(firstItem.allDistance);
|
||
// let dhHtml = "";
|
||
// for (let i = 0; i < firstItem.steps.length; i++) {
|
||
// const item = firstItem.steps[i];
|
||
// dhHtml += item.instruction + ";";
|
||
// }
|
||
},
|
||
error: function (msg: any) {
|
||
ElMessage(msg);
|
||
}
|
||
});
|
||
};
|
||
// 图上拾取起点
|
||
const startPicking = () => {
|
||
if (startGraphic) {
|
||
startGraphic.remove();
|
||
startGraphic = null;
|
||
}
|
||
map.graphicLayer
|
||
.startDraw({
|
||
type: "billboard",
|
||
style: {
|
||
image: "@/assets/images/Mars3DImg/marker/route-start.png",
|
||
horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
|
||
verticalOrigin: Cesium.VerticalOrigin.BOTTOM
|
||
}
|
||
})
|
||
.then((graphic: any) => {
|
||
startGraphic = graphic;
|
||
const point = graphic.point;
|
||
point.format();
|
||
queryRoute();
|
||
mapPosition.value.startPosition = point.lng + "," + point.lat;
|
||
return point.lng + "," + point.lat;
|
||
});
|
||
};
|
||
// 图上拾取终点
|
||
const endPicking = () => {
|
||
if (endGraphic) {
|
||
startGraphic.remove();
|
||
startGraphic = null;
|
||
}
|
||
map.graphicLayer
|
||
.startDraw({
|
||
type: "billboard",
|
||
style: {
|
||
image: "@/assets/images/Mars3DImg/marker/route-end.png",
|
||
horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
|
||
verticalOrigin: Cesium.VerticalOrigin.BOTTOM
|
||
}
|
||
})
|
||
.then((graphic: any) => {
|
||
endGraphic = graphic;
|
||
const point = graphic.point;
|
||
point.format();
|
||
queryRoute();
|
||
mapPosition.value.endPosition = point.lng + "," + point.lat;
|
||
return point.lng + "," + point.lat;
|
||
});
|
||
};
|
||
const closeDiv = () => {
|
||
emits("hiddenConfim");
|
||
};
|
||
onUnmounted(() => {
|
||
map = null;
|
||
});
|
||
</script>
|
||
|
||
<style scoped lang="scss">
|
||
@mixin flex {
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
// 菜单弹框出现动画
|
||
@keyframes fadeIn {
|
||
0% {
|
||
opacity: 0;
|
||
transform: translate3d(100%, 0, 0);
|
||
}
|
||
100% {
|
||
opacity: 1;
|
||
transform: none;
|
||
}
|
||
}
|
||
.main-content {
|
||
position: absolute;
|
||
top: 142px;
|
||
right: 28px;
|
||
padding-bottom: 10px;
|
||
background-image: none !important;
|
||
border: 1px solid #008aff70;
|
||
border-radius: 2px !important;
|
||
background-color: rgba(23, 49, 71, 0.8);
|
||
width: 350px;
|
||
box-shadow: 0 4px 15px 1px #02213bb3;
|
||
animation: fadeIn 1s;
|
||
&-title {
|
||
@include flex;
|
||
width: 100%;
|
||
height: 40px;
|
||
padding: 0 5px 0 10px;
|
||
background-image: url("@/assets/images/Mars3DIcon/subClassTitle.png");
|
||
background-size: 100% 100%;
|
||
background-repeat: no-repeat;
|
||
span {
|
||
font-size: 16px;
|
||
color: #0089fe;
|
||
margin-right: auto;
|
||
}
|
||
:deep() {
|
||
.el-icon {
|
||
cursor: pointer;
|
||
}
|
||
}
|
||
}
|
||
.item-classify {
|
||
padding: 10px;
|
||
margin: 0 auto;
|
||
display: flex;
|
||
flex-direction: column;
|
||
&-item {
|
||
@include flex;
|
||
color: white;
|
||
margin-bottom: 10px;
|
||
> span {
|
||
margin-right: 5px;
|
||
white-space: nowrap;
|
||
}
|
||
:deep() {
|
||
.el-input {
|
||
width: 65%;
|
||
margin-right: 5px;
|
||
}
|
||
.el-checkbox {
|
||
height: auto;
|
||
}
|
||
.el-checkbox__inner,
|
||
.el-radio__inner {
|
||
background-color: transparent;
|
||
}
|
||
.el-checkbox__label,
|
||
.el-radio__label {
|
||
color: white;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
.table-list {
|
||
padding: 0px 10px 10px 10px;
|
||
.table-head {
|
||
border: 2px solid white;
|
||
.tr {
|
||
@include flex;
|
||
.td {
|
||
color: white;
|
||
text-align: center;
|
||
font-size: 14px;
|
||
}
|
||
.td:nth-child(1) {
|
||
width: 30%;
|
||
border-right: 2px solid white;
|
||
}
|
||
.td:nth-child(2) {
|
||
flex: 1;
|
||
}
|
||
}
|
||
}
|
||
.table-body {
|
||
border: 2px solid white;
|
||
border-top: 0;
|
||
.tr:not(:last-child) {
|
||
border-bottom: 2px solid white;
|
||
}
|
||
.tr {
|
||
display: flex;
|
||
.td:nth-child(1) {
|
||
@include flex;
|
||
justify-content: center;
|
||
width: 30%;
|
||
border-right: 2px solid white;
|
||
color: white;
|
||
text-align: center;
|
||
font-size: 14px;
|
||
}
|
||
.td:nth-child(2) {
|
||
flex: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
> div:nth-child(1) {
|
||
border-bottom: 2px solid white;
|
||
padding: 5px;
|
||
}
|
||
> div:nth-child(2) {
|
||
color: white;
|
||
font-size: 14px;
|
||
padding: 5px;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
.speed-set {
|
||
margin-top: 10px;
|
||
@include flex;
|
||
justify-content: center;
|
||
span {
|
||
color: white;
|
||
}
|
||
span:first-child {
|
||
margin-right: 5px;
|
||
}
|
||
span:last-child {
|
||
margin-left: 5px;
|
||
}
|
||
:deep() {
|
||
.el-input-number {
|
||
width: 50%;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
// element 组件样式
|
||
:deep() {
|
||
.el-input__wrapper {
|
||
background-color: transparent !important;
|
||
}
|
||
.el-input__inner {
|
||
color: white;
|
||
}
|
||
.el-input-number__increase,
|
||
.el-input-number__decrease {
|
||
background-color: transparent;
|
||
color: white;
|
||
}
|
||
.el-button {
|
||
background: rgba(51, 89, 181, 0.6);
|
||
border-radius: 6px;
|
||
border: 0;
|
||
}
|
||
}
|
||
</style>
|