feature: 地形分析、通视分析交互组件和逻辑实现

devzpx
zhaipx 2 weeks ago
parent 12872e61ca
commit bb042154cd

@ -24,11 +24,6 @@
cesium="true"
src="public/Cesium/Cesium.js"
></script>
<!-- <script
type="text/javascript"
cesium="true"
src="https://api.tianditu.gov.cn/cdn/demo/sanwei/static/cesium/Cesium.js"
></script> -->
<script
type="text/javascript"
cesium="true"

@ -6,7 +6,6 @@
"scripts": {
"dev": "vite",
"build": "vite build",
"build-localhost": "vite build && node update-index.mjs",
"preview": "vite preview",
"lint": "eslint src",
"fix": "eslint src --fix",

@ -1,6 +1,6 @@
import * as Cesium from 'cesium'
import {Cartesian3} from 'cesium'
import {geojson2kml} from "@/assets/js/DataUtils.js";
import {useStaticStore} from "@/store/staticOptions.js";
export default class MeasureDistance {
constructor(viewer) {
@ -8,17 +8,18 @@ export default class MeasureDistance {
this.scene = viewer.scene
this.isMeasure = false
this.positions = []
this.temPositions = [] // 鼠标移动时产生的临时点
this.vertexEntities = [] // 节点元素
this.lineEntity = undefined // 距离测量折线元素
this.lineEntitys = [] // 距离测量折线元素数组,每完成一次距离测量,将折线元素加入此数组
this.totalDistance = 0 // 总距离
this.temPositions = [] // 距离测量-鼠标移动时产生的临时点
this.vertexEntities = [] // 距离测量-节点元素
this.lineEntity = undefined // 距离测量-折线元素
this.lineEntitys = [] // 距离测量-折线元素数组,每完成一次距离测量,将折线元素加入此数组
this.totalDistance = 0 // 距离测量-总距离
this.activeShapePoints = [] //面积测量多边形顶点
this.activeShapePoints = [] //面积测量-多边形顶点
this.polygon = undefined
this.measAreaStatus = false //是否在测量面积
this.graphics = []
this.analyseGraphs = [] //分析工具的图形容器
this.graphics = [] //图形绘制容器
this.dynamicData = {
lon: undefined,
lat: undefined,
@ -30,15 +31,90 @@ export default class MeasureDistance {
borderWidth: 1,
material: Cesium.Color.GREEN.withAlpha(0.5),
}
}
profileAnalyse(){
//解绑鼠标事件
if(this.handler) this.unRegisterEvents()
let count = 0
let profilePts = [] //点集合
let line = undefined
let movePoint = undefined
this.handler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas)
// 绑定鼠标左击事件
this.handler.setInputAction(e => {
let position = this.viewer.scene.pickPosition(e.position);
if (!position) {
const ellipsoid = this.viewer.scene.globe.ellipsoid;
position = this.viewer.scene.camera.pickEllipsoid(e.position, ellipsoid);
}
if (!position) return;
count++
profilePts.push(position)
let nodeEntity = this._createPointEntity(position, count.toString())
this.viewer.entities.add(nodeEntity)
this.analyseGraphs.push(nodeEntity)
if(!line){
line = this.viewer.entities.add({
polyline: {
positions: new Cesium.CallbackProperty(e => {
return profilePts.concat(movePoint);
}, false),
width: 2,
material: Cesium.Color.YELLOW,
clampToGround: true,
},
})
}
//通视分析仅支持选择两个点
if(useStaticStore().analysisVars.analysisType===2&&profilePts.length===2){
this.unRegisterEvents()
useStaticStore().analysisVars.profilePts = profilePts
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
this.handler.setInputAction(e => {
let position = this.viewer.scene.pickPosition(e.endPosition);
if (!position) {
position = this.viewer.scene.camera.pickEllipsoid(e.startPosition, this.viewer.scene.globe.ellipsoid);
}
if (!position) return;
movePoint = position
this.viewer.scene.requestRender()
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
this.handler.setInputAction(e => {
let position = this.viewer.scene.pickPosition(e.position);
if (!position) {
const ellipsoid = this.viewer.scene.globe.ellipsoid;
position = this.viewer.scene.camera.pickEllipsoid(e.position, ellipsoid);
}
if (!position) return;
count++
profilePts.push(position)
let endEntity = this._createPointEntity(position, count.toString())
this.viewer.entities.add(endEntity)
this.analyseGraphs.push(endEntity)
this.analyseGraphs.push(line)
this.unRegisterEvents()
useStaticStore().analysisVars.profilePts = profilePts
}, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
return false
}
clearProfileEntities(){
this.analyseGraphs.forEach((item)=>{
this.viewer.entities.remove(item)
})
}
updateDynamicData(data){
this.dynamicData.lon = data.lon;
this.dynamicData.lat = data.lat;
this.dynamicData.alt = data.alt;
this.dynamicData.heading = data.heading;
this.viewer.scene.requestRender()
}
addKml(kml_file){
@ -692,7 +768,7 @@ export default class MeasureDistance {
})
this.graphics = []
}
/********************** 图形绘制 ***************************************/
/********************** 实时图形绘制 ***************************************/
drawGraphics(key){
this.viewer.scene.globe.depthTestAgainstTerrain = true
// 清除可能会用到的监听事件
@ -920,4 +996,31 @@ export default class MeasureDistance {
}
}, Cesium.ScreenSpaceEventType.RIGHT_CLICK)
}
_createPointEntity(position, label=""){
return new Cesium.Entity({
position: position,
point: {
pixelSize: 4,
color: Cesium.Color.RED,
outlineColor: Cesium.Color.WHITE,
outlineWidth: 1,
disableDepthTestDistance:99000000,
heightReference:Cesium.HeightReference.CLAMP_TO_GROUND,
},
label: {
text: label,
font: "2rem sans-serif",
fillColor: Cesium.Color.WHITE,
style: Cesium.LabelStyle.FILL,
pixelOffset: new Cesium.Cartesian2(20, -10),
eyeOffset: new Cesium.Cartesian3(0, 0, 0),
horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
scale: 0.5,
clampToGround: true,
heightReference:Cesium.HeightReference.CLAMP_TO_GROUND,
disableDepthTestDistance:99000000,
},
})
}
}

@ -1,238 +0,0 @@
<!--
文件描述地形剖面图组件
创建时间2024/4/23 9:55
创建人Zhaipeixiu
-->
<script setup lang="ts">
import {ref} from 'vue'
import * as echarts from 'echarts'
const emit = defineEmits(['confirmDia'])
let showDia = ref<boolean>(false)
const openDia = (): void => {
showDia.value = true
}
const closeDia = (): void => {
showDia.value = false
}
const confirmDia = (): void => {
emit('confirmDia', '弹窗内容事件处理完毕,信息传给父组件。')
}
/**
* 绘制折线图航线碰撞检测
* @param xData x数组
* @param yData y数组以面积线绘制
* @param yData2 y数组以折线绘制
*/
const drawChart_AirlineDetect = (xData:number[],yData:number[],yData2:number[]) => {
let myChart = echarts.init(document.getElementById('profileChart'))
//
myChart.setOption({
legend:{
show: true,
type: 'plain',
top: '7%',
data:[
{
name: 'groundLine',
itemStyle: 'inherit',
lineStyle: 'inherit',
},
{
name: 'airLine',
itemStyle: 'inherit',
lineStyle: 'inherit',
}]
},
tooltip: {
show: true,
trigger: 'axis',
axisPointer: {
type: 'cross'
},
formatter:'地表高度: {c0}<br>航线高度: {c1}'
},
xAxis: {
data: xData,
name: '距离',
nameTextStyle: {
fontWeight:'bolder',
fontSize: 14
},
nameLocation: 'end',
axisLine:{
onZero: false,
show: true, // 线
symbol: ['none', 'arrow'],
symbolSize: [7, 10]
},
axisLabel: {
formatter: '{value} m',
margin: 5,
},
axisTick: {
show: true, //
inside: true, //
alignWithLabel: true,
lineStyle: {
color: '#000000', //线
type: 'solid', //线线solid线dashed线dotted
},
}
},
yAxis: {
type: 'value',
name: '高度',
nameTextStyle: {
fontWeight:'bolder',
fontSize: 14
},
nameLocation: 'end',
position: 'left',
axisLabel: {
formatter: '{value} m'
},
axisLine: {
show: true,
symbol: ['none', 'arrow'],
symbolSize: [7, 10]
}
},
series: [
{
name:'groundLine',
type: 'line',
data: yData,
areaStyle: {
color: '#37a5fb',
opacity: 0.5
}
},
{
name:'airLine',
type: 'line',
data: yData2,
}
]
});
}
/**
* 绘制折线图地形剖面
* @param xData x数组
* @param yData y数组以面积线绘制
*/
const drawChart_TerrainProfile = (xData:number[],yData:number[]) => {
let myChart = echarts.init(document.getElementById('profileChart'))
//
myChart.setOption({
legend:{
show: true,
type: 'plain',
top: '7%',
data:[
{
name: 'groundLine',
itemStyle: 'inherit',
lineStyle: 'inherit',
},
{
name: 'airLine',
itemStyle: 'inherit',
lineStyle: 'inherit',
}]
},
tooltip: {
show: true,
trigger: 'axis',
axisPointer: {
type: 'cross'
},
formatter:'地表高度: {c0}'
},
xAxis: {
data: xData,
name: '距离',
nameTextStyle: {
fontWeight:'bolder',
fontSize: 14
},
nameLocation: 'end',
axisLine:{
onZero: false,
show: true, // 线
symbol: ['none', 'arrow'],
symbolSize: [7, 10]
},
axisLabel: {
formatter: '{value} m',
margin: 5,
},
axisTick: {
show: true, //
inside: true, //
alignWithLabel: true,
lineStyle: {
color: '#000000', //线
type: 'solid', //线线solid线dashed线dotted
},
}
},
yAxis: {
type: 'value',
name: '高度',
nameTextStyle: {
fontWeight:'bolder',
fontSize: 14
},
nameLocation: 'end',
position: 'left',
axisLabel: {
formatter: '{value} m'
},
axisLine: {
show: true,
symbol: ['none', 'arrow'],
symbolSize: [7, 10]
}
},
series: [
{
name:'groundLine',
type: 'line',
data: yData,
areaStyle: {
color: '#37a5fb',
opacity: 0.5
}
}
]
});
}
// vue3使 <script setup>
// 访使 <script setup> 西 defineExpose
defineExpose({
openDia,
closeDia,
drawChart_AirlineDetect,
drawChart_TerrainProfile
})
</script>
<template>
<div id="profileChart" v-show="showDia">
</div>
</template>
<style scoped>
#profileChart{
width: 700px;
height: 500px;
position: relative;
}
</style>

@ -0,0 +1,196 @@
<!--
文件描述地形剖面图组件
创建时间2024/4/23 9:55
创建人Zhaipeixiu
-->
<script setup lang="ts">
import {nextTick, ref, watch} from "vue";
import {
drawEchartsProfileAnalyse,
drawEchartsVisibility, elevationProfile,
profileAnalyse
} from "@/utils/map/SpatialAnalysis.ts";
import {useStaticStore} from "@/store/staticOptions";
import {useMessage} from "naive-ui";
import {Cartesian3} from "cesium";
let showResultModal = ref(false)
let store = useStaticStore()
let message = useMessage()
let showParamsDialog = ref(false)
let ifInputCoords = ref(false)
//TODO:
let formParams = ref({
interval: 100,
startClearance: 40, //
endClearance: 10, //
})
//
const handleCloseProfile = ()=>{
//
window.measureViewer.clearProfileEntities()
//
store.analysisVars.profilePts = []
store.analysisVars.analysisType = -1
}
//
function executeTopographicAnalyse(polyline:Cartesian3[]) {
//
if(store.analysisVars.analysisType === 1){
//
let res = profileAnalyse(window.viewer, polyline,formParams.value.interval)
//
showResultModal.value = true
nextTick(()=>{
drawEchartsProfileAnalyse(res.distanceArray, res.elevationArray)
})
}
//
else if (store.analysisVars.analysisType === 2){
console.log(store.analysisVars.analysisType)
//
let res = profileAnalyse(window.viewer, polyline,formParams.value.interval)
// 线
let eyeRes = elevationProfile(window.viewer,polyline[0],polyline[1],-1)
eyeRes.elevationArray[0] += Number(formParams.value.startClearance)
eyeRes.elevationArray[1] += Number(formParams.value.endClearance)
//
showResultModal.value = true
nextTick(()=>{
drawEchartsVisibility(res.distanceArray, res.elevationArray, eyeRes.elevationArray[0],eyeRes.elevationArray[1])
})
}
}
watch(()=>store.analysisVars.profilePts, (newValue, OldValue): void => {
console.log("数据变化了", newValue)
if(newValue.length==1) {
message.error('请至少选择两个点')
return;
}
if(newValue.length<1) return;
executeTopographicAnalyse(newValue)
})
function inputCoords(){
ifInputCoords.value = !ifInputCoords.value
}
function pickPoints() {
window.measureViewer.profileAnalyse()
showParamsDialog.value = false
}
defineExpose({
openParamsDialog:()=>{
showParamsDialog.value = true
}
})
const pickedCoords = ref([
{
lon: null,
lat: null
}
])
const onCreate = ()=> {
return {lon: null, lat: null}
}
//
const afterCoordInput = ()=> {
console.log(pickedCoords.value)
//
showParamsDialog.value = false
let polyline: Cartesian3[] = []
pickedCoords.value.forEach((coord) => {
let lon1,lat1
lon1 = Number(coord.lon)
lat1 = Number(coord.lat)
let Certain3 = Cartesian3.fromDegrees(lon1, lat1)
polyline.push(Certain3)
})
//
executeTopographicAnalyse(polyline)
}
</script>
<template>
<n-modal v-model:show="showResultModal" style="width: 50rem; height: 38rem"
preset="card" draggable :mask-closable="false" :on-after-leave="handleCloseProfile"
:title="store.analysisVars.analysisType==2? '通视分析结果':'剖面分析结果'">
<template v-slot:default>
<div id="profileEChart"></div>
</template>
</n-modal>
<n-modal v-model:show="showParamsDialog" style="width: 20rem;" preset="card"
draggable :title="store.analysisVars.analysisType==2? '通视分析参数设置':'剖面分析参数设置'"
:mask-closable="false">
<template v-slot:default>
<n-space>
<n-form ref="formRef" :model="formParams"
label-placement="left" label-width="auto"
require-mark-placement="right-hanging">
<n-form-item label="采样间隔" path="numberParam" >
<n-input round v-model:value="formParams.interval">
<template #suffix>
</template>
</n-input>
</n-form-item>
<n-form-item label="起点离地高度" path="numberParam" v-show="store.analysisVars.analysisType==2">
<n-input round v-model:value="formParams.startClearance">
<template #suffix>
</template>
</n-input>
</n-form-item>
<n-form-item label="终点离地高度" path="numberParam" v-show="store.analysisVars.analysisType==2">
<n-input round v-model:value="formParams.endClearance">
<template #suffix>
</template>
</n-input>
</n-form-item>
</n-form>
</n-space>
<n-row justify-content="space-around">
<n-tooltip placement="bottom" trigger="hover">
<template #trigger>
<n-button type="info" size="small" @click="pickPoints"></n-button>
</template>
<span>左键单击选点右键结束</span>
</n-tooltip>
<n-button :type="ifInputCoords? 'warning':'info'" size="small"
@click="inputCoords">{{ifInputCoords? '取消输入':'输入坐标' }}
</n-button>
<n-button type="info" size="small">导入文件</n-button>
</n-row>
<n-divider v-show="ifInputCoords"/>
<n-dynamic-input v-model:value="pickedCoords" :on-create="onCreate" v-show="ifInputCoords"
:min="2" :max="store.analysisVars.analysisType==2? 2:10">
<template #default="{ value }">
<n-input size="tiny" v-model:value="value.lon" placeholder="经度"/>
<n-input size="tiny" v-model:value="value.lat" placeholder="纬度"/>
</template>
</n-dynamic-input>
<n-row justify-content="space-around" style="margin-top:.5rem;">
<n-button type="primary" size="small" v-show="ifInputCoords"
@click="afterCoordInput" :disabled="pickedCoords.length<2"> 确定 </n-button>
</n-row>
</template>
</n-modal>
</template>
<style scoped>
#profileEChart{
width: 47rem;
height: 37rem;
position: relative;
margin-top: -2rem;
}
</style>

@ -0,0 +1,22 @@
<script lang="ts">
import {defineComponent} from 'vue'
import {drawEchartsVisibility} from "@/utils/map/SpatialAnalysis.ts";
export default defineComponent({
name: "UnitTest",
mounted() {
let xdt = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20];
let ydt = [23,34,3,4,5,6,7,8,9,23,123,122,90,45,34,16,17,142,53,2,10];
drawEchartsVisibility(xdt, ydt, 30, 20);
}
})
</script>
<template>
<div id="Echarts-UnitTest"
style="width: 100%; height: 40rem;position: absolute;"></div>
</template>
<style scoped>
</style>

@ -7,12 +7,11 @@
-->
<template>
<div id="cesium-viewer" ref="viewerDivRef"></div>
<ProfileAnalysis ref="profileChart"></ProfileAnalysis>
</template>
<script setup lang="ts">
import {onMounted, ref, watch} from 'vue'
import {Viewer, Ion, CustomDataSource, Cartesian3} from 'cesium'
import {onMounted, ref} from 'vue'
import {Viewer, Ion, CustomDataSource} from 'cesium'
import 'cesium/Build/Cesium/Widgets/widgets.css'
import {
TDTLayerType,
@ -22,20 +21,15 @@ import {
} from '@/utils/map/TDTProvider'
import { initViewer, perfViewer, showNavigator } from '@/utils/map/sceneViewer'
import { flyToChina } from '@/utils/map/camera'
import ProfileAnalysis from "@/components/ProfileAnalysis.vue";
import MeasureDistance from "@/assets/js/cesium-map/measureDistance";
const viewerDivRef = ref<HTMLDivElement>()
let viewer: Viewer
// window.CESIUM_BASE_URL = 'node_modules/cesium/Build/Cesium/'
window.CESIUM_BASE_URL = 'public/Cesium/' //
window.CESIUM_BASE_URL = 'public/Cesium/'
Ion.defaultAccessToken =
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI3YjU4MjJlMS0wMWE4LTRhOWQtYjQ1OC04MTgzMzFhMzQ5ZjAiLCJpZCI6MTE1ODUxLCJpYXQiOjE2NjkyMDM1MzN9.8ajEuv3VKYg8wvFiQlUWWY6Ng6JfY4PuVgRyStL1B-E'
//
let profileChart = ref<InstanceType<typeof ProfileAnalysis> | null>(null)
let linePoints = ref<number>(-1)
onMounted(() => {
//
@ -63,29 +57,17 @@ onMounted(() => {
// window
window.viewer = viewer
window.measureViewer = new MeasureDistance(viewer);
//
// const drawPolyline = new CreatePolyline(viewer,false,true,{})
// linePoints.value = drawPolyline.positions.length
// drawPolyline.start()
// profileChart.value?.openDia()
//
// 线
// profileChart.value?.drawChart_TerrainProfile([0,1,2,3,4,5,6,7,8,9],
// [-70,800,300,400,23,232,435,243,234,343])
})
watch(linePoints, (newValue, OldValue): void => {
console.log("数据变化了",linePoints.value, newValue, OldValue)
})
</script>
</script>
<!--样式修改-->
<style scoped>
#cesium-viewer {
width: 100%;
height: 100%;
}
</style>

@ -13,26 +13,17 @@ import {ref} from "vue";
import {useStaticStore} from "@/store/staticOptions.js";
import {login, requestAirline} from "@/assets/js/request.js";
import {dataProcess, getAirline} from "@/assets/js/websocketProtocol.ts";
import SpatialAnalysis from "@/components/SpatialAnalysis.vue";
const message = useMessage();
let file, SceneValue;
let SceneValue;
let showModal = ref(false);
let hasPlane = ref(false);
let store = useStaticStore();
// --------------------------------------
const spatialAnalyse= ref(null)
SceneValue = ref('untrace');
let sceneOptions= [
{
label: '第三视角跟随',
value: 'fallow'
},{
label: '不跟随',
value: 'untrace'
}
];
function handleSceneSelect(key){
if(!hasPlane.value) return;
@ -42,50 +33,9 @@ function handleSceneSelect(key){
window.viewer.trackedEntity = window.viewer.entities.getById('websocket-flying-plane');
}
}
let EditOptions = [
{
label: '查询航线',
key: 'requestLine'
},
{
label: '航线管理',
key: 'manage'
}]
let layerValue = ref('layer1');
let barIsOpen = ref(true);
let MeasureOptions = [
{
label: '距离测量',
key: 'distance'
},
{
label: '面积测量',
key: 'area'
},
{
label: '清除',
key: 'clear'
},
]
let DrawOptions = [
{
label: '多边形',
key: 'polygon'
},
{
label: '矩形',
key: 'rec'
},{
label: '圆形',
key: 'circle'
},
{
label: '清除',
key: 'clear'
},
]
function handleEditSelect(key) {
if(key === 'requestLine') {
getUavAirline()
@ -94,6 +44,19 @@ function handleEditSelect(key) {
// 线
}
}
function handleAnalyseSelect(key) {
if(key === 'visibility') {
store.analysisVars.analysisType = 2
//
spatialAnalyse.value?.openParamsDialog()
}
if(key === 'profile') {
store.analysisVars.analysisType = 1
//
spatialAnalyse.value?.openParamsDialog()
}
}
function handleDrawSelect(key) {
if(key === 'clear') {
@ -173,6 +136,10 @@ function measureArea() {
window.measureViewer.activateAreaMeasure();
}
/**
* 连接websocket
* @returns {Promise<void>}
*/
async function connectWebSocket() {
await login(store.temp.userName, store.temp.password).then(rsp => {
let resData = JSON.parse(rsp.data.data)
@ -185,9 +152,9 @@ async function connectWebSocket() {
})
if(sessionStorage.getItem('token') === 'err') return
store.server.ws = new WebSocket('ws://123.57.54.1:8048/htfp/websocket/uavGlobal/sysUser003', sessionStorage.getItem('token'))
store.webskt.ws = new WebSocket('ws://123.57.54.1:8048/htfp/websocket/uavGlobal/sysUser003', sessionStorage.getItem('token'))
// store.server.ws = new WebSocket('ws://'+store.server.ws_config.address+':'+store.server.ws_config.port);
store.server.ws.onmessage = (event) => {
store.webskt.ws.onmessage = (event) => {
//....
let data = dataProcess(JSON.parse(event.data))
console.log(data);
@ -203,12 +170,18 @@ async function connectWebSocket() {
};
}
/**
* 关闭websocket连接
*/
function closeWS(){
if(store.server.ws){
store.server.ws.close();
if(store.webskt.ws){
store.webskt.ws.close();
}
}
/**
* 请求航线接口
*/
function getUavAirline() {
if(sessionStorage.getItem('uavId')){
requestAirline(sessionStorage.getItem('uavId')).then(rsp => {
@ -219,11 +192,13 @@ function getUavAirline() {
message.warning('当前未连接飞机')
}
}
</script>
<template>
<n-flex id="panel">
<n-popselect v-model:value="layerValue" :options="store.templateValue.layerOptions" size="medium">
<n-popselect v-model:value="layerValue" :options="store.menuOptions.layerOptions" size="medium">
<n-button tertiary circle type="warning">
<template #icon>
<n-icon><Layers/></n-icon>
@ -240,31 +215,30 @@ function getUavAirline() {
</template>
<span> 添加数据 </span>
</n-tooltip>
<n-tooltip placement="bottom" trigger="hover">
<template #trigger>
<n-dropdown :options="store.menuOptions.AnalyzeOptions" @select="handleAnalyseSelect">
<n-button tertiary circle type="warning">
<template #icon>
<n-icon><TerrainSharp/></n-icon>
</template>
</n-button>
</template>
<span> 地形分析 </span>
</n-tooltip>
<n-dropdown :options="EditOptions" @select="handleEditSelect">
</n-dropdown>
<n-dropdown :options="store.menuOptions.EditOptions" @select="handleEditSelect">
<n-button tertiary circle type="warning">
<template #icon>
<n-icon><CreateOutline/></n-icon>
</template>
</n-button>
</n-dropdown>
<n-dropdown :options="MeasureOptions" @select="handleSelect">
<n-dropdown :options="store.menuOptions.MeasureOptions" @select="handleSelect">
<n-button tertiary circle type="warning">
<template #icon>
<n-icon><RulerAlt/></n-icon>
</template>
</n-button>
</n-dropdown>
<n-dropdown :options="DrawOptions" @select="handleDrawSelect">
<n-dropdown :options="store.menuOptions.DrawOptions" @select="handleDrawSelect">
<n-button tertiary circle type="warning">
<template #icon>
<n-icon><DrawPolygon/></n-icon>
@ -281,8 +255,8 @@ function getUavAirline() {
</template>
<span> WebSocket配置 </span>
</n-tooltip>
<n-popselect v-model:value="SceneValue" :options="sceneOptions" @update:value="handleSceneSelect"
size="medium">
<n-popselect v-model:value="SceneValue" :options="store.menuOptions.sceneOptions"
@update:value="handleSceneSelect" size="medium">
<!-- :disabled="!hasPlane" -->
<n-button tertiary circle type="warning">
@ -309,15 +283,15 @@ function getUavAirline() {
</template>
<div style="margin: 2rem 2rem .5rem 1rem">
<n-space>
<n-form ref="formRef" :model="store.server.ws_config"
<n-form ref="formRef" :model="store.webskt.ws_config"
label-placement="left"
label-width="auto"
require-mark-placement="right-hanging">
<n-form-item label="服务器地址">
<n-input v-model:value="store.server.ws_config.address" placeholder="127.0.0.1"/>
<n-input v-model:value="store.webskt.ws_config.address" placeholder="127.0.0.1"/>
</n-form-item>
<n-form-item label="端口号">
<n-input-number v-model:value="store.server.ws_config.port" placeholder=8000 />
<n-input-number v-model:value="store.webskt.ws_config.port" placeholder=8000 />
</n-form-item>
</n-form>
</n-space>
@ -328,6 +302,8 @@ function getUavAirline() {
</n-space>
</div>
</n-modal>
<SpatialAnalysis ref="spatialAnalyse"></SpatialAnalysis>
</template>
<style scoped>

@ -7,6 +7,11 @@ const router = createRouter({
path: '/',
name: 'Home',
component: ()=>import( '@/components/HomePage.vue'),
},
{
path: '/test',
name: 'Test',
component: ()=>import('@/components/UnitTest.vue')
}
]
});

@ -1,5 +1,6 @@
import {defineStore} from "pinia";
import cesiumAirPlane from "@/assets/models/Cesium_Air.glb";
import {ref} from "vue";
export const useStaticStore = defineStore('staticOptions',{
state: ()=>{
return {
@ -8,10 +9,42 @@ export const useStaticStore = defineStore('staticOptions',{
password:"sysUser003",
token:"",
},
server: {
menuOptions:{
EditOptions: [
{label: '查询航线', key: 'requestLine'},
{label: '航线管理', key: 'manage'}
],
sceneOptions:[
{label: '第三视角跟随', value: 'fallow'},
{label: '不跟随', value: 'untrace'}
],
MeasureOptions:[
{label: '距离测量', key: 'distance'},
{label: '面积测量', key: 'area'},
{label: '清除', key: 'clear'},
],
DrawOptions:[
{label: '多边形', key: 'polygon'},
{label: '矩形', key: 'rec'},
{label: '圆形', key: 'circle'},
{label: '清除', key: 'clear'},
],
AnalyzeOptions:[
{label: '通视分析', key: 'visibility'},
{label: '地形剖面', key: 'profile'}
],
//图层选项,动态
layerOptions: [
{
label: '图层1',
value: 'layer1'
}
],
},
webskt: {
ws: null,
isOpen: false,
ws_config:{
ws_config: {
address: '127.0.0.1',
port: 8000,
},
@ -20,15 +53,10 @@ export const useStaticStore = defineStore('staticOptions',{
defaultAirPlane: cesiumAirPlane,
},
hasPlane: false,
templateValue:{
//图层选项,动态
layerOptions: [
{
label: '图层1',
value: 'layer1'
}
],
analysisVars: {
//当前的空间分析类型剖面1 通视2
analysisType: -1,
profilePts: [], //地形分析的点数组
}
}
}

@ -6,6 +6,7 @@
import {getDistance, getElevation} from "@/utils/map/geocomputation.ts";
import {Cartesian3, Viewer} from "cesium";
import * as echarts from "echarts";
type ProfileResult = {
distanceArray:number[],
elevationArray:number[],
@ -24,23 +25,29 @@ export function elevationProfile(viewer: Viewer, start:Cartesian3, end:Cartesian
let distanceFromStart:number[] = [] //断点至起点的距离m
// 计算首尾点距离 m
let totalLen = getDistance(start, end) * 1000
// 获取起点高度
breakPointsHeight.push(getElevation(viewer, start))
// 获取起点高度及其与起点的距离
breakPointsHeight.push(Math.round(getElevation(viewer, start)))
distanceFromStart.push(0)
//获取中间断点的高度及其与起点的距离
if(interval > 0){
//断点数量
let breakNum = Math.floor(totalLen/interval)
// 如果采样间隔小于首尾点距离,则获取每个断点的坐标 并获取其高度
if(breakNum>=1){
for (let i = 1; i <= breakNum; i++) {
for (let i = 1; i < breakNum; i++) {
let breakP = Cartesian3.lerp(start, end, i/breakNum, new Cartesian3())
breakPointsHeight.push(getElevation(viewer, breakP))
distanceFromStart.push(getDistance(start,breakP)*1000)
breakPointsHeight.push(Math.round(getElevation(viewer, breakP))) //单位 米
distanceFromStart.push(Math.round(getDistance(start,breakP)*1000)) //单位 米
}
}
}
// 获取终点高度
breakPointsHeight.push(getElevation(viewer, end))
distanceFromStart.push(totalLen)
return { distanceArray:distanceFromStart, elevationArray:breakPointsHeight }
// 获取终点高度及其与起点的距离
breakPointsHeight.push(Math.round(getElevation(viewer, end)))
distanceFromStart.push(Math.round(totalLen))
return { distanceArray: distanceFromStart, elevationArray: breakPointsHeight }
}
/**
@ -50,12 +57,18 @@ export function elevationProfile(viewer: Viewer, start:Cartesian3, end:Cartesian
* @param interval 线 m
* @return 线线
*/
export function profileAnalyse(viewer: Viewer, polyline:Cartesian3[],interval: number){
let result:ProfileResult = { distanceArray:[], elevationArray:[] }
for (let i = 0; i < polyline.length - 2; i++) {
let temp = elevationProfile(viewer,polyline[i],polyline[i+1],interval)
export function profileAnalyse(viewer: Viewer, polyline:Cartesian3[], interval: number){
let result: ProfileResult = { distanceArray:[], elevationArray:[] }
let temp_dis = 0 //每两点之间的距离
for (let i = 0; i <= polyline.length - 2; i++) {
let temp = elevationProfile(viewer, polyline[i], polyline[i+1], interval)
result.elevationArray = result.elevationArray.concat(temp.elevationArray)
result.distanceArray = result.distanceArray.concat(temp.distanceArray)
temp.distanceArray.forEach(distance => {
result.distanceArray.push(distance+temp_dis)
})
if(temp.distanceArray.length > 0){
temp_dis += temp.distanceArray[temp.distanceArray.length-1]
}
}
return result
}
@ -66,19 +79,21 @@ export function profileAnalyse(viewer: Viewer, polyline:Cartesian3[],interval: n
* @param viewer
* @param viewpoint
* @param target
* @param h 1m
* @param h1 m
* @param h2 m
* @param curvature
* @param breakNum 100
*/
export function visibilityAnalyse(viewer: Viewer, viewpoint:Cartesian3, target:Cartesian3,
h= 1, breakNum = 100) {
h1:number, h2:number,curvature:boolean, breakNum = 100) {
// 获取视点高度和目标点高度
let viewpointH = getElevation(viewer, viewpoint) + h
let targetH = getElevation(viewer, target)
if (viewpointH === -9999.2024 + h ) {
let viewpointH = getElevation(viewer, viewpoint) + h1
let targetH = getElevation(viewer, target) + h2
if (viewpointH === -9999.2024 + h1 ) {
console.log("无法获取视点海拔高度!")
return undefined
}
if (targetH === -9999.2024 ) {
if (targetH === -9999.2024 + h2 ) {
console.log("无法获取目标点海拔高度!")
return undefined
}
@ -98,5 +113,350 @@ export function visibilityAnalyse(viewer: Viewer, viewpoint:Cartesian3, target:C
return false
}
}
if(curvature){
let Rmax = 2.898 * (Math.sqrt(h1)+Math.sqrt(h2))
if (Rmax < totalLen/1000.0) return false
}
return true
}
/**
* 线
* @param xData x
* @param yData y线
*/
export function drawEchartsProfileAnalyse(xData:number[], yData:number[]) {
let myChart = echarts.init(document.getElementById('profileEChart'))
// 绘制图表
myChart.setOption({
legend: {
show: true,
type: 'plain',
top: '5%',
data:[
{
name: 'groundLine',
itemStyle: 'inherit',
lineStyle: 'inherit',
},
]
},
tooltip: {
show: true,
trigger: 'axis',
axisPointer: {
type: 'cross'
},
formatter:'地表高度: {c0}'
},
xAxis: {
data: xData,
name: '距离/米',
nameTextStyle: {
fontWeight:'bolder',
fontSize: 14
},
nameLocation: 'end',
axisLine:{
onZero: false,
show: true, // 是否显示坐标轴轴线
symbol: ['none', 'arrow'],
symbolSize: [7, 10]
},
axisLabel: {
formatter: '{value}',
margin: 5,
},
axisTick: {
show: true, // 是否显示坐标轴刻度
inside: true, // 坐标轴刻度是否朝内,默认朝外
alignWithLabel: true,
lineStyle: {
color: '#000000', //刻度线的颜色
type: 'solid', //坐标轴线线的类型solid实线类型dashed虚线类型dotted点状类型
},
}
},
yAxis: {
type: 'value',
name: '高度/米',
nameTextStyle: {
fontWeight:'bolder',
fontSize: 14
},
nameLocation: 'end',
position: 'left',
axisLabel: {
formatter: '{value}'
},
axisLine: {
show: true,
symbol: ['none', 'arrow'],
symbolSize: [7, 10]
}
},
series: [
{
name:'groundLine',
type: 'line',
data: yData,
areaStyle: {
color: '#37a5fb',
opacity: 0.5
}
}
]
});
}
/**
* 线线
* @param xData x
* @param yData y线
* @param yData2 y线
*/
export const drawEchartsAirlineDetect = (xData:number[],yData:number[],yData2:number[]) => {
let myChart = echarts.init(document.getElementById('profileEChart'))
// 绘制图表
myChart.setOption({
legend:{
show: true,
type: 'plain',
top: '7%',
data:[
{
name: 'groundLine',
itemStyle: 'inherit',
lineStyle: 'inherit',
},
{
name: 'airLine',
itemStyle: 'inherit',
lineStyle: 'inherit',
}]
},
tooltip: {
show: true,
trigger: 'axis',
axisPointer: {
type: 'cross'
},
formatter:'地表高度: {c0}<br>航线高度: {c1}'
},
xAxis: {
data: xData,
name: '距离',
nameTextStyle: {
fontWeight:'bolder',
fontSize: 14
},
nameLocation: 'end',
axisLine:{
onZero: false,
show: true, // 是否显示坐标轴轴线
symbol: ['none', 'arrow'],
symbolSize: [7, 10]
},
axisLabel: {
formatter: '{value} m',
margin: 5,
},
axisTick: {
show: true, // 是否显示坐标轴刻度
inside: true, // 坐标轴刻度是否朝内,默认朝外
alignWithLabel: true,
lineStyle: {
color: '#000000', //刻度线的颜色
type: 'solid', //坐标轴线线的类型solid实线类型dashed虚线类型dotted点状类型
},
}
},
yAxis: {
type: 'value',
name: '高度',
nameTextStyle: {
fontWeight:'bolder',
fontSize: 14
},
nameLocation: 'end',
position: 'left',
axisLabel: {
formatter: '{value} m'
},
axisLine: {
show: true,
symbol: ['none', 'arrow'],
symbolSize: [7, 10]
}
},
series: [
{
name:'groundLine',
type: 'line',
data: yData,
areaStyle: {
color: '#37a5fb',
opacity: 0.5
}
},
{
name:'airLine',
type: 'line',
data: yData2,
}
]
});
}
/**
* 线线
* @param xData x
* @param yData y线
* @param startHeight 线
* @param endHeight 线
*/
export const drawEchartsVisibility = (xData:number[], yData:number[],startHeight:number, endHeight:number) => {
console.group()
console.log(0, startHeight)
console.log(xData.at(-1), endHeight)
console.groupEnd()
console.log(xData)
let myChart = echarts.init(document.getElementById('profileEChart')) //Echarts-UnitTest
// 绘制图表
myChart.setOption({
legend: {
show: true,
type: 'plain',
top: '7%',
data: [
{ //地形剖面图例
name: '剖面线',
itemStyle: {
fontSize: 18
},
lineStyle: 'inherit',
},
{ //视线图例
name: '视线',
lineStyle: {
type: 'dotted',
width: 3,
color: '#f8364d'
},
itemStyle: {
fontSize: 18
}
}
],
selectedMode: false, //图例选择模式关闭
},
tooltip: {
show: true,
trigger: 'axis',
axisPointer: {
type: 'cross'
},
formatter:'地表高度: {c0}'
},
xAxis: {
boundaryGap : false,
max: (value:any)=>{
return value.max * 1.01;
},
min: 0,
data: xData,
name: '距离/m',
nameTextStyle: {
fontWeight:'bolder',
fontSize: 14
},
nameLocation: 'end',
axisLine:{
onZero: false,
show: true, // 是否显示坐标轴轴线
symbol: ['none', 'arrow'],
symbolSize: [7, 10]
},
axisLabel: {
formatter: '{value}',
margin: 5,
},
axisTick: {
show: true, // 是否显示坐标轴刻度
inside: true, // 坐标轴刻度是否朝内,默认朝外
alignWithLabel: true,
lineStyle: {
color: '#000000', //刻度线的颜色
type: 'solid', //坐标轴线线的类型solid实线类型dashed虚线类型dotted点状类型
},
}
},
yAxis: {
max: (value:any)=>{
return Math.floor(Math.max(value.max,startHeight,endHeight)*1.01)
},
min: (value:any)=>{
return Math.floor(Math.min(value.min,startHeight,endHeight)*0.99)
},
type: 'value',
name: '高度/ m',
nameTextStyle: {
fontWeight:'bolder',
fontSize: 14
},
nameLocation: 'end',
position: 'left',
axisLabel: {
formatter: '{value}'
},
axisLine: {
show: true,
symbol: ['none', 'arrow'],
symbolSize: [7, 10]
}
},
series: [
{
name:'剖面线',
type: 'line',
data: yData,
areaStyle: {
color: '#37a5fb',
opacity: 0.5
},
markLine: {
data: [
[
{coord: ['0', startHeight.toString()]},
{coord: [xData.at(-1)?.toString(), endHeight.toString()]}
// Markline中的坐标点必须为string否则异常
]
],
symbol:['circle', 'arrow'],
label:{
formatter: "模 拟 视 线",
show: true,
position: 'middle',
fontSize: 15,
fontWeight: 'bold',
color: '#e73d3d',
},
lineStyle: { //标注线样式
type: 'dashed',
color: 'red',
with: 10
}
},
},
{ /* 视线的series */
type: 'line',
symbol: 'none',
name: '视线',
color: 'transparent'
}
],
});
}

@ -1,16 +0,0 @@
import fs from 'fs';
console.time('转换用时');
const distPath = './dist/index.html'; //打包路径的index.html
let htmlText = fs.readFileSync(distPath, 'utf8');
let resultText = '';
let htmlArr = htmlText.match(/.*\n/g) || [];
htmlArr.forEach(str => {
str = str.replace(/\s?nomodule\s?/g,' ');
str = str.replace(/\s?crossorigin\s?/g,' ');
str = str.replace(/data-src/g,'src');
if(!/type="module"/i.test(str))
resultText += str;
});
fs.writeFileSync(distPath,resultText,'utf8');
console.timeEnd('转换用时');

@ -19,7 +19,7 @@ export default defineConfig({
}),
],
base: './',
build:{
build: {
target: ['es2015', 'chrome63'],
},
resolve: {

Loading…
Cancel
Save