9 files modified
6 files added
| New file |
| | |
| | | /** |
| | | * 全站http配置 |
| | | * |
| | | * axios参数说明 |
| | | * isSerialize是否开启form表单提交 |
| | | * isToken是否需要token |
| | | */ |
| | | import axios, { AxiosRequestConfig, AxiosResponse } from 'axios' |
| | | import store from '@/store/user' |
| | | import router from '@/router'; |
| | | import { serialize } from '@/utils/common' |
| | | import { getToken } from '@/utils/auth'; |
| | | import { ElMessage } from 'element-plus'; |
| | | import website from '@/config/website'; |
| | | import { Base64 } from 'js-base64' |
| | | // import NProgress from 'nprogress'; |
| | | // import 'nprogress/nprogress.css'; |
| | | import { ELocalStorageKey } from '@/utils/enums'; |
| | | export * from './type' |
| | | |
| | | // 自定义响应类型 |
| | | interface CustomAxiosResponse<T = any> extends AxiosResponse<T> { |
| | | data: T |
| | | } |
| | | |
| | | // 定义你的请求类型 |
| | | interface CustomAxiosRequestConfig extends AxiosRequestConfig { |
| | | // 是否开启鉴权 |
| | | authorization?: boolean |
| | | } |
| | | |
| | | type CustomAxiosConfig = { |
| | | [x: string]: any |
| | | <T = any, R = CustomAxiosResponse<T>, D = CustomAxiosRequestConfig>(config: D): Promise<R> |
| | | } |
| | | |
| | | const REQUEST_ID = 'X-Request-Id' |
| | | |
| | | const getAuthToken = () => { |
| | | return localStorage.getItem(ELocalStorageKey.Token) |
| | | } |
| | | |
| | | const instance: CustomAxiosConfig = axios.create({ |
| | | // withCredentials: true, |
| | | headers: { |
| | | 'Content-Type': 'application/json', |
| | | }, |
| | | baseURL: '/api', |
| | | // timeout: 12000, |
| | | withCredentials: true, |
| | | }) |
| | | |
| | | instance.interceptors.request.use( |
| | | (config: { |
| | | data: any |
| | | method: string |
| | | token: any |
| | | text: boolean |
| | | json: boolean |
| | | meta: { |
| | | isSerialize: boolean |
| | | isToken: boolean |
| | | noCookie: boolean |
| | | } |
| | | headers: { [x: string]: string | null } |
| | | url: string |
| | | authorization: boolean |
| | | }) => { |
| | | if(config.url.includes('h5')) { |
| | | config.url = '/drone-h5' + config.url |
| | | } else if (!config.url.includes('/blade-') && !config.url.includes('https://') && !config.url.includes('http://')) { |
| | | config.url = '/drone-yw' + config.url |
| | | } |
| | | const authorization = config.authorization === false |
| | | if (!authorization) { |
| | | config.headers.Authorization = `Basic ${Base64.encode(`${website.clientId}:${website.clientSecret}`)}` |
| | | } |
| | | // 让每个请求携带token |
| | | const meta = config.meta || {} |
| | | const isToken = meta.isToken === false |
| | | if (getToken() && !isToken) { |
| | | config.headers[website.tokenHeader] = 'bearer ' + getToken() |
| | | } |
| | | if (meta.noCookie) { |
| | | delete config.headers.cookie |
| | | } |
| | | // headers中配置text请求 |
| | | if (config.text === true) { |
| | | config.headers['Content-Type'] = 'text/plain' |
| | | } |
| | | // headers配置JSON请求 |
| | | if (config.json === true) { |
| | | config.headers['Content-Type'] = 'application/json' |
| | | } |
| | | // 水情预报接口调用 |
| | | if (config.token) { |
| | | config.headers.Authorization = config.token |
| | | } |
| | | // headers中配置serialize为true开启序列化 |
| | | if (config.method === 'post' && meta.isSerialize === true) { |
| | | config.data = serialize(config.data) |
| | | } |
| | | // config.headers[ELocalStorageKey.Token] = localStorage.getItem(ELocalStorageKey.Token) |
| | | // config.headers[REQUEST_ID] = uuidv4() |
| | | // config.baseURL = CURRENT_CONFIG.baseURL |
| | | return config |
| | | }, |
| | | (error: any) => { |
| | | return Promise.reject(error) |
| | | }, |
| | | ) |
| | | |
| | | instance.interceptors.response.use( |
| | | (res: { data: { code: any; msg: any; message: any; error_description: any }; status: any; config: { url: string; meta: { noCookie: boolean } } }) => { |
| | | const status = res.data.code || res.status |
| | | const message = res.data.msg || res.data.message || res.data.error_description || '未知错误' |
| | | // 如果是401则跳转到登录页面 |
| | | // const store = useMyStore() |
| | | if (status === 401) { |
| | | store.dispatch('FedLogOut').then(() => router.push({ path: '/login' })) |
| | | // return |
| | | } |
| | | // 如果请求为非200否者默认统一处理 |
| | | if (status !== 200) { |
| | | // 排除海康 code 为 0 情况 |
| | | if (status === 0) { |
| | | return res |
| | | } |
| | | if ('meta' in res.config && 'noCookie' in res.config.meta && res.config.meta.noCookie === true) { |
| | | return Promise.reject(new Error(message)) |
| | | } |
| | | if (message === '缺失令牌,鉴权失败') { |
| | | // message({ |
| | | // message: '登录信息已过期,请重新登录', |
| | | // type: 'warning', |
| | | // }) |
| | | ElMessage.warning('登录信息已过期,请重新登录') |
| | | } |
| | | if ( |
| | | ![ |
| | | '没有收到消息回复。', |
| | | '未知消息', |
| | | '设备端退出drc模式', |
| | | '未能恢复航路作业。错误码:: 319042', |
| | | 'success', |
| | | '缺失令牌,鉴权失败', |
| | | ].includes(message) |
| | | ) { |
| | | ElMessage.warning(message) |
| | | } |
| | | return Promise.reject(new Error(message)) |
| | | } |
| | | // console.info('URL: ' + response.config.baseURL + response.config.url, '\nData: ', response.data, '\nResponse:', response) |
| | | // const whiteList = ['/manage/api/v1/live/streams/stop'] |
| | | // if (whiteList.includes(response.config.url)) { |
| | | // return Promise.reject(response.data) |
| | | // } |
| | | // 处理消息返回 |
| | | // if (response.data.code && !response.data.success) { |
| | | // message.error(response.data.message) |
| | | // } |
| | | // if (response.data.code && response.data.code !== 0) { |
| | | // message.error(response.data.message) |
| | | // } |
| | | return res |
| | | }, |
| | | (err: { |
| | | config: { headers: { [x: string]: any }; url: any; method: any } |
| | | response: { data: { message: string; result: { message: string } }; status: number } |
| | | }) => { |
| | | const requestId = err?.config?.headers && err?.config?.headers[REQUEST_ID] |
| | | if (requestId) { |
| | | console.info(REQUEST_ID, ':', requestId) |
| | | } |
| | | console.info('url: ', err?.config?.url, `【${err?.config?.method}】 \n>>>> err: `, err) |
| | | |
| | | let description = '-' |
| | | if (err.response?.data && err.response.data.message) { |
| | | description = err.response.data.message |
| | | } |
| | | if (err.response?.data && err.response.data.result) { |
| | | description = err.response.data.result.message |
| | | } |
| | | // @See: https://github.com/axios/axios/issues/383 |
| | | if (!err.response || !err.response.status) { |
| | | ElMessage.error('网络异常,请检查后端服务后重试') |
| | | return |
| | | } |
| | | if (err.response?.status !== 200) { |
| | | ElMessage.error(`错误码: ${err.response?.status}`) |
| | | } |
| | | // if (err.response?.status === 403) { |
| | | // // window.location.href = '/' |
| | | // } |
| | | if (err.response?.status === 401) { |
| | | console.error(err.response) |
| | | store.dispatch('FedLogOut').then(() => router.push({ path: '/login' })) |
| | | // const flag: number = Number(localStorage.getItem(ELocalStorageKey.Flag)) |
| | | // switch (flag) { |
| | | // case EUserType.Web: |
| | | // router.push(ERouterName.PROJECT_LIST) |
| | | // break |
| | | // case EUserType.Pilot: |
| | | // router.push(ERouterName.PILOT) |
| | | // break |
| | | // } |
| | | } |
| | | return Promise.reject(err) |
| | | }, |
| | | ) |
| | | |
| | | export default instance |
| | |
| | | * isToken是否需要token |
| | | */ |
| | | import axios, { AxiosRequestConfig, AxiosResponse } from 'axios' |
| | | import store from '@/store/user' |
| | | // import store from '@/store/user' |
| | | import { useMyStore } from '@/store' |
| | | import router from '@/router'; |
| | | import { serialize } from '@/utils/common' |
| | | import { getToken } from '@/utils/auth'; |
| | |
| | | if (config.method === 'post' && meta.isSerialize === true) { |
| | | config.data = serialize(config.data) |
| | | } |
| | | // config.headers[ELocalStorageKey.Token] = localStorage.getItem(ELocalStorageKey.Token) |
| | | // config.headers[REQUEST_ID] = uuidv4() |
| | | // config.baseURL = CURRENT_CONFIG.baseURL |
| | | return config |
| | | }, |
| | | (error: any) => { |
| | |
| | | const status = res.data.code || res.status |
| | | const message = res.data.msg || res.data.message || res.data.error_description || '未知错误' |
| | | // 如果是401则跳转到登录页面 |
| | | // const store = useMyStore() |
| | | if (status === 401) { |
| | | store.dispatch('FedLogOut').then(() => router.push({ path: '/login' })) |
| | | // return |
| | | if (status == 401) { |
| | | router.push({ path: '/login' }) |
| | | } |
| | | // 如果请求为非200否者默认统一处理 |
| | | if (status !== 200) { |
| | |
| | | return Promise.reject(new Error(message)) |
| | | } |
| | | if (message === '缺失令牌,鉴权失败') { |
| | | // message({ |
| | | // message: '登录信息已过期,请重新登录', |
| | | // type: 'warning', |
| | | // }) |
| | | ElMessage.warning('登录信息已过期,请重新登录') |
| | | } |
| | | if ( |
| | |
| | | } |
| | | return Promise.reject(new Error(message)) |
| | | } |
| | | // console.info('URL: ' + response.config.baseURL + response.config.url, '\nData: ', response.data, '\nResponse:', response) |
| | | // const whiteList = ['/manage/api/v1/live/streams/stop'] |
| | | // if (whiteList.includes(response.config.url)) { |
| | | // return Promise.reject(response.data) |
| | | // } |
| | | // 处理消息返回 |
| | | // if (response.data.code && !response.data.success) { |
| | | // message.error(response.data.message) |
| | | // } |
| | | // if (response.data.code && response.data.code !== 0) { |
| | | // message.error(response.data.message) |
| | | // } |
| | | return res |
| | | }, |
| | | (err: { |
| | |
| | | if (err.response?.data && err.response.data.result) { |
| | | description = err.response.data.result.message |
| | | } |
| | | // @See: https://github.com/axios/axios/issues/383 |
| | | if (!err.response || !err.response.status) { |
| | | ElMessage.error('网络异常,请检查后端服务后重试') |
| | | return |
| | | } |
| | | if (err.response?.status !== 200) { |
| | | if (err.response.data?.msg) { |
| | | ElMessage.error(err.response.data?.msg) |
| | | } else if (err.response.data?.error_description) { |
| | | ElMessage.error(err.response.data?.error_description) |
| | | } else { |
| | | ElMessage.error(`错误码: ${err.response?.status}`) |
| | | } |
| | | // if (err.response?.status === 403) { |
| | | // // window.location.href = '/' |
| | | // } |
| | | if (err.response?.status === 401) { |
| | | console.error(err.response) |
| | | store.dispatch('FedLogOut').then(() => router.push({ path: '/login' })) |
| | | // const flag: number = Number(localStorage.getItem(ELocalStorageKey.Flag)) |
| | | // switch (flag) { |
| | | // case EUserType.Web: |
| | | // router.push(ERouterName.PROJECT_LIST) |
| | | // break |
| | | // case EUserType.Pilot: |
| | | // router.push(ERouterName.PILOT) |
| | | // break |
| | | // } |
| | | } |
| | | if (err.response?.status == 401) { |
| | | router.push({ path: '/login' }) |
| | | } |
| | | return Promise.reject(err) |
| | | }, |
| | |
| | | import store, { storeKey } from './store' |
| | | import { createInstance } from '../root' |
| | | import * as Cesium from 'cesium'; |
| | | import Vant from 'vant'; |
| | | import 'vant/lib/index.css'; // 引入样式文件 |
| | | |
| | | // 初始化 Cesium Viewer |
| | | window.cesiumViewer = null |
| | |
| | | const app = createInstance(App) // 引入css |
| | | |
| | | app.use(ElementPlus); |
| | | app.use(Vant); |
| | | app.use(store, storeKey) |
| | | app.use(router) |
| | | |
| | |
| | | // }, |
| | | { |
| | | path: '/', |
| | | redirect: '/login', |
| | | }, |
| | | { |
| | | path: '/login', |
| | | name: ERouterName.LOGIN, |
| | | component: () => import('@/views/Login.vue') |
| | | }, |
| | |
| | | // 获取机场/遥控器sn |
| | | let localData = ref<any>(window.localStorage.getItem('sbInfo')) |
| | | const sbInfo: deviceInfo = JSON.parse(localData.value) as deviceInfo; |
| | | // console.log('哒哒哒', sbInfo) |
| | | |
| | | // 无人机sn |
| | | const sn = ref<String>(sbInfo.sn) |
| | | // 机场sn |
| New file |
| | |
| | | <template> |
| | | <div class="drone-pilot-details" :style="{ width: screenWidth,height: screenHeight}"> |
| | | <!-- 横屏模式 --> |
| | | <div id="landscapeBox" class="landscape-box" :style="{ width: screenWidth + 'px', height: (screenHeight) + 'px'}"> |
| | | <div id="videoModule" class="l-video" :style="{ width: screenWidthVideo + 'px', height: (screenHeightVideo) + 'px'}"> |
| | | <video v-show="videoUrl" ref="videoPlayerBig" :style="{ width: screenWidthVideo + 'px', height: screenHeightVideo + 'px'}" controls autoplay muted playsinline style="text-align: left; object-fit: fill"> |
| | | Your browser is too old which doesn't support HTML5 video. |
| | | </video> |
| | | <el-empty v-show="videoUrl == ''" description="当前设备已关机,无法进行直播" :image="imageUrl"></el-empty> |
| | | </div> |
| | | <div class="l-map" :style="{ width: screenWidthVideo + 'px', height: (screenHeightVideo) + 'px'}"> |
| | | <div id="cesiumContainerBigMap" :style="{ width: screenWidthMap + 'px', height: (screenHeightMap) + 'px'}"></div> |
| | | <div class="l-zp" id="lZp"> |
| | | <comPass :cesiumViewe="globalViewer"/> |
| | | </div> |
| | | <div class="l-zoom" id="lZoom"> |
| | | <div class="zoom"> |
| | | <img v-if="!isBigMap" src="@/assets/images/fang.png" @touchstart="changeModelMap" /> |
| | | <img v-else src="@/assets/images/suo.png" @touchstart="changeModelMap" /> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <div class="right-box"> |
| | | <ptzControl :sn="sn" :osdVisible="sbInfo" /> |
| | | <controlConsole :sn="sn" :osdVisible="sbInfo" :cesiumViewe="globalViewer" /> |
| | | </div> |
| | | </div> |
| | | <!-- 竖屏 --> |
| | | <!-- <div class="portrait-screen" v-else> |
| | | <div class="l-video" :style="{ width: screenWidth + 'px', height: (screenHeight/2)+40 + 'px'}"> |
| | | <video v-show="videoUrl" ref="videoPlayerBig" :style="{ width: screenWidth + 'px', height: (screenHeight/2)+40 + 'px'}" controls autoplay muted playsinline style="text-align: left; object-fit: fill"> |
| | | Your browser is too old which doesn't support HTML5 video. |
| | | </video> |
| | | <el-empty v-show="videoUrl == ''" description="当前设备已关机,无法进行直播" :image="imageUrl"></el-empty> |
| | | </div> |
| | | <div class="l-map" :style="{ width: screenWidth + 'px', height: (screenHeight/2)-40 + 'px'}"> |
| | | <div id="cesiumContainerMap"></div> |
| | | <div class="l-zp" id="lZp"> |
| | | <comPass :cesiumViewe="globalViewer"/> |
| | | </div> |
| | | </div> |
| | | <div class="right-box"> |
| | | <ptzControl :sn="sn" :osdVisible="sbInfo" /> |
| | | <controlConsole :sn="sn" :osdVisible="sbInfo" /> |
| | | </div> |
| | | </div> --> |
| | | </div> |
| | | </template> |
| | | |
| | | <script lang="ts" setup> |
| | | import { ref, onMounted, onBeforeUnmount, nextTick, reactive, computed, watch } from 'vue'; |
| | | import { EDeviceTypeName, EHmsLevel } from '@/types/enums' |
| | | import { getRoot } from '../../root' |
| | | import { startLivestream, getLiveVideoUrl } from '@/api/manage' |
| | | import { CURRENT_CONFIG as config } from '@/api/http/config' |
| | | // import videojs from 'video.js' |
| | | // import 'video.js/dist/video-js.css'; |
| | | import { getLnglatDist, cartesian3Convert, getLnglatAltitude, createCircleBillboard } from '@/utils/cesium/mapUtil' |
| | | import cesiumOperation from '@/utils/cesium-tsa.js'; |
| | | const { _init, viewerDestory } = cesiumOperation(); |
| | | import * as Cesium from 'cesium'; |
| | | |
| | | // import { cesiumOperation } from '@/hooks/use-cesium-tsa' |
| | | // const { _init } = cesiumOperation(); |
| | | import { useMyStore } from '@/store' |
| | | import { getWaylineFile } from '@/api/manage' |
| | | import comPass from './components/comPass.vue' |
| | | import ptzControl from './components/ptzControl.vue' |
| | | import controlConsole from './components/controlConsole.vue' |
| | | import { useConnectWebSocket } from '@/utils/websocket/connect-websocket' |
| | | import { getWebsocketUrl } from '@/websocket/util/config' |
| | | import { EBizCode, ELocalStorageKey, ERouterName } from '@/types' |
| | | import VConsole from 'vconsole'; |
| | | import { any } from 'video.js/dist/types/utils/events'; |
| | | import { analyzeKmzFile, XMLToJSON } from '@/utils/cesium/kmz.js' |
| | | import { initPointWayLine } from './hooks/initPointWayline' |
| | | import { initPlanarWayline } from './hooks/initPlanarWayline' |
| | | import { droneFly } from './hooks/droneFly' |
| | | import flvjs from 'flv.js'; |
| | | const { pointWayline } = initPointWayLine() |
| | | const { planarWayline } = initPlanarWayline() |
| | | const { initDock } = droneFly() |
| | | |
| | | const { |
| | | removeAllPoint, |
| | | removeAllDataSource, |
| | | globalCesium, |
| | | getEntityById, |
| | | removeById, |
| | | loadLAYER, |
| | | } = cesiumOperation() |
| | | |
| | | // const vConsole = new VConsole(); |
| | | |
| | | let globalViewer = null |
| | | |
| | | // 使用 ref 来存储屏幕宽度和高度 |
| | | const screenWidth = ref(window.innerWidth); |
| | | const screenHeight = ref(window.innerHeight); |
| | | // 地图切换 |
| | | const screenWidthMap = ref((window.innerHeight/2)*2)// ref(window.innerWidth); |
| | | const screenHeightMap = ref((window.innerHeight/2)); |
| | | // 视频切换 |
| | | const screenWidthVideo = ref(window.innerWidth); |
| | | const screenHeightVideo = ref(window.innerHeight/2); |
| | | const isBigMap = ref(false) |
| | | |
| | | // 更新屏幕尺寸的函数 |
| | | const updateScreenSize = () => { |
| | | screenWidth.value = window.innerWidth; |
| | | screenHeight.value = window.innerHeight; |
| | | if (isLandscape.value) { //横屏 |
| | | screenWidthMap.value = window.innerWidth/2 |
| | | screenHeightMap.value = window.innerHeight |
| | | screenWidthVideo.value = window.innerWidth/2 |
| | | screenHeightVideo.value = window.innerHeight |
| | | } else {//竖屏 |
| | | screenWidthMap.value = (window.innerHeight/2)*2 |
| | | screenHeightMap.value = (window.innerHeight/2) |
| | | screenWidthVideo.value = window.innerWidth |
| | | screenHeightVideo.value = (window.innerHeight/2) |
| | | } |
| | | }; |
| | | |
| | | const store = useMyStore() |
| | | // 获取路由 |
| | | const root = getRoot() |
| | | |
| | | interface deviceInfo { |
| | | dockSn: string; |
| | | latitude?:number;// 机场经纬度 |
| | | longitude?:number; |
| | | height?:number; // 机场高度 |
| | | isOnline: boolean; |
| | | name: string; // 可选字段 |
| | | sn: string; |
| | | workspaceId:string; |
| | | children?: { }; |
| | | } |
| | | // 获取机场/遥控器sn |
| | | let localData = ref<any>(window.localStorage.getItem('sbInfo')) |
| | | const sbInfo: deviceInfo = JSON.parse(localData.value) as deviceInfo; |
| | | |
| | | // 无人机sn |
| | | const sn = ref<String>(sbInfo.sn) |
| | | // 机场sn |
| | | const dockSn = ref<String>(sbInfo.dockSn) |
| | | |
| | | // 记录横竖屏模式 |
| | | const isLandscape = ref(false); |
| | | |
| | | // 记录视频地址 |
| | | const videoUrl = ref<String>(''); |
| | | |
| | | // 设置图片地址 |
| | | const imageUrl = new URL('@/assets/images/norecord.png', import.meta.url).href |
| | | |
| | | const imageMapSrc = new URL('@/assets/images/mapdock.png', import.meta.url).href |
| | | |
| | | const meMapSrc = new URL('@/assets/images/me.png', import.meta.url).href |
| | | |
| | | const jgbMapSrc = new URL('@/assets/images/jgb.png', import.meta.url).href |
| | | |
| | | const mapswitching = new URL('@/assets/images/mapswitching.png', import.meta.url).href |
| | | |
| | | const videoWH = ref<any>(null) |
| | | |
| | | // 视频宽高 |
| | | const drawCanvasWidth = ref<Number>(0) |
| | | const drawCanvasHeight = ref<Number>(0) |
| | | |
| | | // 鹰眼视频 |
| | | const timer = null; |
| | | const videoPlayer = ref<any>(null); // 视频播放器实例 |
| | | let webrtcPlayer = null; // video.js 播放器 |
| | | |
| | | // 大屏幕视频 |
| | | const timerBig = null; |
| | | const videoPlayerBig = ref<any>(null); // 视频播放器实例 |
| | | let webrtcPlayerBig = null |
| | | |
| | | let player = null; // video.js 播放器 |
| | | |
| | | // 无人机实时飞行链接 |
| | | let ssLinePath = ref(null) |
| | | |
| | | // 实时定位 |
| | | let viewDroneInfo = {} |
| | | // 机场高度 |
| | | let droneHeight = ref(0) |
| | | |
| | | // 监听 |
| | | let connectWs = ref<any>(null) |
| | | |
| | | let workspaceId = ref<String>(sbInfo.workspaceId) |
| | | localStorage.setItem('bs_workspace_id', workspaceId.value) |
| | | // store.commit('SET_SELECTED_WORKSPACE_ID', workspaceId.value); |
| | | |
| | | // RTCWEB 加载视频 |
| | | const playBig = () => { |
| | | webrtcPlayerBig = new window.ZLMRTCClient.Endpoint({ |
| | | element: videoPlayerBig.value, // video 标签 |
| | | debug: true, // 是否打印日志 |
| | | zlmsdpUrl: videoUrl.value, //流地址 |
| | | simulecast: false, |
| | | useCamera: false, |
| | | audioEnable: true, |
| | | videoEnable: true, |
| | | recvOnly: true, |
| | | usedatachannel: false, |
| | | }) |
| | | webrtcPlayerBig.on( |
| | | window.ZLMRTCClient.Events.WEBRTC_ICE_CANDIDATE_ERROR, |
| | | (e) => { |
| | | // ICE 协商出错 |
| | | console.error('ICE 协商出错') |
| | | // this.eventcallbacK('ICE ERROR', 'ICE 协商出错') |
| | | }, |
| | | ) |
| | | |
| | | webrtcPlayerBig.on( |
| | | window.ZLMRTCClient.Events.WEBRTC_ON_REMOTE_STREAMS, |
| | | (e) => { |
| | | //获取到了远端流,可以播放 |
| | | console.log('播放成功', e.streams) |
| | | // this.eventcallbacK('playing', '播放成功') |
| | | }, |
| | | ) |
| | | |
| | | webrtcPlayerBig.on( |
| | | window.ZLMRTCClient.Events.WEBRTC_OFFER_ANWSER_EXCHANGE_FAILED, |
| | | (e) => { |
| | | // offer anwser 交换失败 |
| | | console.error('offer anwser 交换失败', e) |
| | | // this.eventcallbacK('OFFER ANSWER ERROR ', 'offer anwser 交换失败') |
| | | if (e.code == -400 && e.msg == '流不存在') { |
| | | console.log('流不存在') |
| | | timerBig = setTimeout(() => { |
| | | webrtcPlayerBig.close() |
| | | playBig() |
| | | }, 100) |
| | | } |
| | | }, |
| | | ) |
| | | |
| | | webrtcPlayerBig.on( |
| | | window.ZLMRTCClient.Events.WEBRTC_ON_LOCAL_STREAM, |
| | | (s) => { |
| | | // 获取到了本地流 |
| | | |
| | | // document.getElementById('selfVideo').srcObject=s; |
| | | // this.eventcallbacK('LOCAL STREAM', '获取到了本地流') |
| | | }, |
| | | ) |
| | | } |
| | | // const play = () => { |
| | | // webrtcPlayer = new window.ZLMRTCClient.Endpoint({ |
| | | // element: videoPlayer.value, // video 标签 |
| | | // debug: true, // 是否打印日志 |
| | | // zlmsdpUrl: videoUrl.value, //Rtc地址 |
| | | // simulecast: false, |
| | | // useCamera: false, |
| | | // audioEnable: true, |
| | | // videoEnable: true, |
| | | // recvOnly: true, |
| | | // usedatachannel: false, |
| | | // }) |
| | | // webrtcPlayer.on( |
| | | // window.ZLMRTCClient.Events.WEBRTC_ICE_CANDIDATE_ERROR, |
| | | // (e) => { |
| | | // // ICE 协商出错 |
| | | // console.error('ICE 协商出错') |
| | | // // this.eventcallbacK('ICE ERROR', 'ICE 协商出错') |
| | | // }, |
| | | // ) |
| | | |
| | | // webrtcPlayer.on( |
| | | // window.ZLMRTCClient.Events.WEBRTC_ON_REMOTE_STREAMS, |
| | | // (e) => { |
| | | // //获取到了远端流,可以播放 |
| | | // console.log('播放成功', e.streams) |
| | | // // this.eventcallbacK('playing', '播放成功') |
| | | // }, |
| | | // ) |
| | | |
| | | // webrtcPlayer.on( |
| | | // window.ZLMRTCClient.Events.WEBRTC_OFFER_ANWSER_EXCHANGE_FAILED, |
| | | // (e) => { |
| | | // // offer anwser 交换失败 |
| | | // console.error('offer anwser 交换失败', e) |
| | | // // this.eventcallbacK('OFFER ANSWER ERROR ', 'offer anwser 交换失败') |
| | | // if (e.code == -400 && e.msg == '流不存在') { |
| | | // console.log('流不存在') |
| | | // timer = setTimeout(() => { |
| | | // webrtcPlayer.close() |
| | | // play() |
| | | // }, 100) |
| | | // } |
| | | // }, |
| | | // ) |
| | | |
| | | // webrtcPlayer.on( |
| | | // window.ZLMRTCClient.Events.WEBRTC_ON_LOCAL_STREAM, |
| | | // (s) => { |
| | | // }, |
| | | // ) |
| | | // } |
| | | const videoUrlFlv = ref<String>('') |
| | | // 加载该设备的视频信息 |
| | | const loadVideo = async () => { |
| | | await getLiveVideoUrl(sn.value).then((res) => { |
| | | if (res.data.code != 0) return |
| | | // 获取高度 |
| | | if (videoWH.value) { |
| | | drawCanvasWidth.value = videoWH.value.offsetWidth; |
| | | drawCanvasHeight.value = videoWH.value.offsetHeight; |
| | | } |
| | | videoUrl.value = res.data.data.rtcs_url |
| | | videoUrlFlv.value = res.data.data.url |
| | | // 播放 |
| | | // play() |
| | | playBig() |
| | | // flv |
| | | // initFlvPlayer() |
| | | }) |
| | | } |
| | | |
| | | // flv |
| | | const videoPlayerFlv = ref(null); // 引用 video 元素 |
| | | let flvPlayer = null; // 用于存储 flv.js 播放器实例 |
| | | // 初始化 flv.js 播放器 |
| | | |
| | | const initFlvPlayer = () => { |
| | | if (flvjs.isSupported()) { |
| | | const videoElement = videoPlayerFlv.value; // 获取 video 元素 |
| | | flvPlayer = flvjs.createPlayer({ |
| | | type: 'flv', // 视频格式 |
| | | url: videoUrlFlv.value, // FLV 视频地址 |
| | | }); |
| | | flvPlayer.attachMediaElement(videoElement); // 绑定 video 元素 |
| | | flvPlayer.load(); // 加载视频 |
| | | flvPlayer.play(); // 播放视频 |
| | | } else { |
| | | console.error('Your browser does not support flv.js.'); |
| | | } |
| | | }; |
| | | |
| | | //=============================== |
| | | |
| | | // 监听ws |
| | | const messageHandler = async (payload: any) => { |
| | | // if (!payload) { |
| | | // return |
| | | // } |
| | | switch (payload.biz_code) { |
| | | case EBizCode.GatewayOsd: { // 遥控器 |
| | | store.commit('SET_GATEWAY_INFO', payload.data) |
| | | break |
| | | } |
| | | case EBizCode.DeviceOsd: { |
| | | store.commit('SET_DEVICE_INFO', payload.data) |
| | | store.commit('SET_WS_MESSAGE', payload) |
| | | break |
| | | } |
| | | case EBizCode.DockOsd: { // 机场 |
| | | store.commit('SET_DOCK_INFO', payload.data) |
| | | break |
| | | } |
| | | } |
| | | |
| | | } |
| | | |
| | | const webSorketUrl = getWebsocketUrl() + '&workspace-id=' + workspaceId.value |
| | | // 监听ws 消息 |
| | | useConnectWebSocket(messageHandler, webSorketUrl) |
| | | |
| | | |
| | | let savedMapState = null; // 保存地图状态 |
| | | // 销毁之前先保存状态 |
| | | const destroyViewer = () => { |
| | | if ( window.cesiumViewer) { |
| | | // 保存当前地图状态(如相机位置) |
| | | // savedMapState = window.cesiumViewer.camera.view; |
| | | window.cesiumViewer.destroy(); |
| | | window.cesiumViewer = null; |
| | | } |
| | | } |
| | | |
| | | // 地图全屏切换 |
| | | const changeModelMap = () => { |
| | | isBigMap.value = !isBigMap.value |
| | | const cesiumContainer = document.getElementById('cesiumContainerBigMap'); |
| | | const lZp = document.getElementById('lZp'); |
| | | const lZoom = document.getElementById('lZoom'); |
| | | |
| | | if (isBigMap.value) { // 全屏 |
| | | cesiumContainer.style.position = 'fixed'; |
| | | cesiumContainer.style.top = '0'; |
| | | cesiumContainer.style.left = '0'; |
| | | cesiumContainer.style.width = '100%'; |
| | | cesiumContainer.style.height = '100%'; |
| | | lZp.style.right = '40%'; |
| | | lZoom.style.left = '0'; |
| | | } else { // 缩放 |
| | | cesiumContainer.style.position = 'static'; |
| | | cesiumContainer.style.width = `${screenWidthMap.value}` |
| | | cesiumContainer.style.height = `${screenHeight.value}` |
| | | lZp.style.right = '16%'; |
| | | lZoom.style.left = '50%'; |
| | | |
| | | } |
| | | } |
| | | |
| | | const checkOrientation = async () => { |
| | | const landscapeBox = document.getElementById('landscapeBox'); |
| | | const cesiumContainer = document.getElementById('cesiumContainerBigMap'); |
| | | const videoModule = document.getElementById('videoModule'); |
| | | const lZp = document.getElementById('lZp'); |
| | | const lZoom = document.getElementById('lZoom'); |
| | | // 横屏再加载视频请求 |
| | | // destroyViewer() |
| | | |
| | | // loadVideo() |
| | | if (window.innerWidth > window.innerHeight) { |
| | | isLandscape.value = true; |
| | | updateScreenSize() |
| | | landscapeBox.style.display = 'flex'; |
| | | lZp.style.right = '16%'; |
| | | lZoom.style.display = 'block'; |
| | | lZoom.style.position = 'fixed'; |
| | | // await nextTick(); |
| | | // await _init('cesiumContainerBigMap'); |
| | | // await getWrjSsLx(); |
| | | } else { |
| | | isLandscape.value = false; |
| | | updateScreenSize() |
| | | landscapeBox.style.display = 'inherit'; |
| | | lZp.style.right = '32%'; |
| | | lZoom.style.display = 'none'; |
| | | // landscapeBox.style.width = `${screenWidth}` |
| | | // landscapeBox.style.height = `${screenHeight}` |
| | | // videoModule.style.width = `${screenWidth}` |
| | | // videoModule.style.height = `${screenHeight}` |
| | | // await nextTick(); |
| | | // await _init('cesiumContainerMap'); |
| | | // await getWrjSsLx(); |
| | | } |
| | | }; |
| | | |
| | | // 获取实时航线 |
| | | const getWrjSsLx = () => { |
| | | getWaylineFile(sbInfo.sn).then((res:any) => { |
| | | if (res.code != 200) return |
| | | ssLinePath.value = res.data |
| | | globalViewer = window.cesiumViewer |
| | | // 先有航线,再飞行 |
| | | generateCourse() |
| | | }) |
| | | } |
| | | |
| | | // 切换航线轨迹 |
| | | const courseTrack = (data:any) => { |
| | | if (window.cesiumViewer== null) return |
| | | const currentSn = dockSn.value |
| | | const longitude = data.dockInfo[currentSn]?.basic_osd?.longitude || null |
| | | const latitude = data.dockInfo[currentSn]?.basic_osd?.latitude || null |
| | | const height = data.dockInfo[currentSn]?.basic_osd?.height || null |
| | | let getLongOk = ref(0) |
| | | if (!getEntityById('drone_dock')) { |
| | | getLnglatAltitude(longitude, latitude, window.cesiumViewer).then(res => { |
| | | removeById('drone_dock') |
| | | window.cesiumViewer.entities.add({ |
| | | position: globalCesium.Cartesian3.fromDegrees( |
| | | longitude, |
| | | latitude, |
| | | res.height, |
| | | ), |
| | | id: 'drone_dock', |
| | | billboard: { |
| | | image: imageMapSrc, |
| | | outlineWidth: 0, |
| | | width: 36, |
| | | height: 36, |
| | | scale: 1.0, |
| | | } |
| | | }) |
| | | window.cesiumViewer.scene.camera.setView({ |
| | | destination: globalCesium.Cartesian3.fromDegrees( |
| | | Number(longitude), |
| | | Number(latitude), |
| | | 10000.0, |
| | | ), |
| | | }) |
| | | droneHeight.value = res.height |
| | | }) |
| | | } else { |
| | | // 当机场坐标存在时,判断获取的位置与机场坐标位置是否一致,若不是,重新更新位置 |
| | | let dornePoint = cartesian3Convert(getEntityById('drone_dock')._position._value, window.cesiumViewer,) |
| | | getLongOk = getLnglatDist( |
| | | dornePoint.longitude, |
| | | dornePoint.latitude, |
| | | longitude, |
| | | latitude, |
| | | ) |
| | | if (getLongOk.value > 100) { |
| | | removeById('drone_dock') |
| | | } |
| | | } |
| | | if (getLongOk.value > 100) return |
| | | initDock(data, sn.value, dockSn.value, workspaceId.value, ssLinePath.value, viewDroneInfo) |
| | | } |
| | | |
| | | // 监听 |
| | | watch(() => store.state.deviceState, (newValue, oldValue) => { |
| | | // 试试 |
| | | if (newValue) { |
| | | nextTick() |
| | | courseTrack(newValue) |
| | | } |
| | | }, {deep: true}) |
| | | |
| | | watch(() => store.state.wsMessage, (newValue, oldValue) => { |
| | | // 控制台报 Expected longitude to be typeof number, actual typeof was undefined 是因为这里没有及时拿到longitude数据 |
| | | if (newValue) { |
| | | viewDroneInfo.longitude = newValue?.longitude |
| | | viewDroneInfo.latitude = newValue?.latitude |
| | | viewDroneInfo.elevation = Number(newValue?.elevation.toFixed(2)) + droneHeight.value |
| | | } |
| | | }, {deep: true}) |
| | | |
| | | |
| | | |
| | | // 生成航线轨迹 |
| | | const generateCourse = async () => { |
| | | console.log('有木有航线值', ssLinePath.value) |
| | | if (!ssLinePath.value) return |
| | | const { fileInfoObj } = await analyzeKmzFile(`${ssLinePath.value}?_t=${new Date().getTime()}`) |
| | | const xmlStr = await fileInfoObj['wpmz/template.kml'] |
| | | const xmlJson = XMLToJSON(xmlStr)?.['Document'] |
| | | const placemark = xmlJson.Folder.Placemark |
| | | console.log('看看航线值', placemark.Polygon) |
| | | if (placemark?.Polygon) { |
| | | // return 'planar' |
| | | planarWayline(ssLinePath.value, sbInfo.longitude, sbInfo.latitude) |
| | | } else { |
| | | // return 'point' |
| | | pointWayline(ssLinePath.value, sbInfo.longitude, sbInfo.latitude) |
| | | } |
| | | } |
| | | |
| | | const me_latitude = ref(0); |
| | | const me_longitude = ref(0); |
| | | // 获取自身定位 |
| | | const getLocation = () => { |
| | | if (window.cesiumViewer == null) return |
| | | if (navigator.geolocation) { |
| | | navigator.geolocation.getCurrentPosition( |
| | | (position) => { |
| | | me_latitude.value = position.coords.latitude; |
| | | me_longitude.value = position.coords.longitude; |
| | | // 开始给自己定位 |
| | | getLnglatAltitude(me_longitude.value, me_latitude.value, window.cesiumViewer).then(res => { |
| | | removeById('me_coordinate') |
| | | window.cesiumViewer.entities.add({ |
| | | position: globalCesium.Cartesian3.fromDegrees( |
| | | me_longitude.value, |
| | | me_latitude.value, |
| | | res.height || 0, |
| | | ), |
| | | id: 'me_coordinate', |
| | | billboard: { |
| | | image: meMapSrc, |
| | | outlineWidth: 0, |
| | | width: 36, |
| | | height: 36, |
| | | scale: 1.0, |
| | | } |
| | | }) |
| | | }) |
| | | }, |
| | | (err) => { |
| | | console.log('定位有误') |
| | | } |
| | | ); |
| | | } else { |
| | | console.log('您的浏览器不支持地理定位') |
| | | } |
| | | }; |
| | | |
| | | onMounted(async () => { |
| | | window.addEventListener('orientationchange', checkOrientation); |
| | | window.addEventListener('resize', checkOrientation); // 兼容某些设备 |
| | | checkOrientation(); // 初始化时检查一次 |
| | | loadVideo() |
| | | await nextTick(); |
| | | await _init('cesiumContainerBigMap'); |
| | | await getWrjSsLx(); |
| | | |
| | | await getLocation() |
| | | |
| | | }); |
| | | |
| | | onBeforeUnmount(() => { |
| | | window.removeEventListener('orientationchange', checkOrientation); |
| | | window.removeEventListener('resize', checkOrientation); |
| | | if (player) { |
| | | player.dispose(); |
| | | } |
| | | if (flvPlayer) { |
| | | flvPlayer.pause(); // 暂停播放 |
| | | flvPlayer.unload(); // 卸载视频 |
| | | flvPlayer.detachMediaElement(); // 解绑 video 元素 |
| | | flvPlayer.destroy(); // 销毁播放器 |
| | | flvPlayer = null; |
| | | } |
| | | }); |
| | | |
| | | |
| | | |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .drone-pilot-details { |
| | | .landscape-box { |
| | | position: relative; |
| | | display: flex; |
| | | .l-map { |
| | | .l-zp { |
| | | position: fixed; |
| | | right: 16%; |
| | | bottom: 0; |
| | | } |
| | | .l-zoom { |
| | | position: fixed; |
| | | bottom: 0; |
| | | left: 50%; |
| | | .zoom { |
| | | width: 2rem; |
| | | height: 2rem; |
| | | border-radius: 3px; |
| | | background-color: rgba(0, 0, 0, 0.8); |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | overflow: hidden; |
| | | cursor: pointer; |
| | | pointer-events: all; |
| | | img { |
| | | width: 1.6rem; |
| | | height: 1.6rem; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | .l-video { |
| | | .el-empty { |
| | | width: 100%; |
| | | height: 100%; |
| | | padding: 0; |
| | | ::v-deep(.el-empty__image) { |
| | | width: 50%; |
| | | height: 50%; |
| | | } |
| | | ::v-deep(.el-empty__description) { |
| | | margin: 0; |
| | | } |
| | | } |
| | | } |
| | | #cesiumContainerBigMap { |
| | | width: 100%; |
| | | height: 100%; |
| | | overflow: hidden; |
| | | |
| | | :deep() { |
| | | .cesium-viewer { |
| | | width: 100%; |
| | | height: 100%; |
| | | |
| | | .cesium-viewer-cesiumWidgetContainer { |
| | | width: 100%; |
| | | height: 100%; |
| | | |
| | | .cesium-widget { |
| | | width: 100%; |
| | | height: 100%; |
| | | |
| | | canvas { |
| | | width: 100%; |
| | | height: 100%; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | #cesiumContainerMap { |
| | | width: 100%; |
| | | height: 100%; |
| | | overflow: hidden; |
| | | |
| | | :deep() { |
| | | .cesium-viewer { |
| | | width: 100%; |
| | | height: 100%; |
| | | |
| | | .cesium-viewer-cesiumWidgetContainer { |
| | | width: 100%; |
| | | height: 100%; |
| | | |
| | | .cesium-widget { |
| | | width: 100%; |
| | | height: 100%; |
| | | |
| | | canvas { |
| | | width: 100%; |
| | | height: 100%; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | .portrait-screen { |
| | | .l-map { |
| | | .l-zp { |
| | | position: fixed; |
| | | right: 28%; |
| | | bottom: 0; |
| | | } |
| | | .l-zoom { |
| | | position: fixed; |
| | | bottom: 0; |
| | | .zoom { |
| | | width: 2rem; |
| | | height: 2rem; |
| | | border-radius: 3px; |
| | | background-color: rgba(0, 0, 0, 0.8); |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | overflow: hidden; |
| | | cursor: pointer; |
| | | pointer-events: all; |
| | | img { |
| | | width: 1.6rem; |
| | | height: 1.6rem; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | </style> |
| New file |
| | |
| | | <template> |
| | | <div class="drone-pilot-details" :style="{ width: screenWidth,height: screenHeight}"> |
| | | <!-- 横屏模式 --> |
| | | <div id="landscapeBox" class="landscape-box" :style="{ width: screenWidth + 'px', height: (screenHeight) + 'px'}"> |
| | | <div id="videoModule" class="l-video" :style="{ width: screenWidthVideo + 'px', height: (screenHeightVideo) + 'px'}"> |
| | | <video v-show="videoUrl" ref="videoPlayerBig" :style="{ width: screenWidthVideo + 'px', height: screenHeightVideo + 'px'}" controls autoplay muted playsinline style="text-align: left; object-fit: fill"> |
| | | Your browser is too old which doesn't support HTML5 video. |
| | | </video> |
| | | <el-empty v-show="videoUrl == ''" description="当前设备已关机,无法进行直播" :image="imageUrl"></el-empty> |
| | | </div> |
| | | <div class="l-map" :style="{ width: screenWidthMap + 'px', height: (screenHeightMap) + 'px'}"> |
| | | <div id="cesiumContainerBigMap"></div> |
| | | <div class="l-zp" id="lZp"> |
| | | <comPass :cesiumViewe="globalViewer"/> |
| | | </div> |
| | | <div class="l-zoom" id="lZoom"> |
| | | <div class="zoom"> |
| | | <img v-if="!isBigMap" src="@/assets/images/fang.png" @touchstart="changeModelMap" /> |
| | | <img v-else src="@/assets/images/suo.png" @touchstart="changeModelMap" /> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <div class="right-box"> |
| | | <ptzControl :sn="sn" :osdVisible="sbInfo" /> |
| | | <controlConsole :sn="sn" :osdVisible="sbInfo" :cesiumViewe="globalViewer" /> |
| | | </div> |
| | | </div> |
| | | <!-- 竖屏 --> |
| | | <!-- <div class="portrait-screen" v-else> |
| | | <div class="l-video" :style="{ width: screenWidth + 'px', height: (screenHeight/2)+40 + 'px'}"> |
| | | <video v-show="videoUrl" ref="videoPlayerBig" :style="{ width: screenWidth + 'px', height: (screenHeight/2)+40 + 'px'}" controls autoplay muted playsinline style="text-align: left; object-fit: fill"> |
| | | Your browser is too old which doesn't support HTML5 video. |
| | | </video> |
| | | <el-empty v-show="videoUrl == ''" description="当前设备已关机,无法进行直播" :image="imageUrl"></el-empty> |
| | | </div> |
| | | <div class="l-map" :style="{ width: screenWidth + 'px', height: (screenHeight/2)-40 + 'px'}"> |
| | | <div id="cesiumContainerMap"></div> |
| | | <div class="l-zp" id="lZp"> |
| | | <comPass :cesiumViewe="globalViewer"/> |
| | | </div> |
| | | </div> |
| | | <div class="right-box"> |
| | | <ptzControl :sn="sn" :osdVisible="sbInfo" /> |
| | | <controlConsole :sn="sn" :osdVisible="sbInfo" /> |
| | | </div> |
| | | </div> --> |
| | | </div> |
| | | </template> |
| | | |
| | | <script lang="ts" setup> |
| | | import { ref, onMounted, onBeforeUnmount, nextTick, reactive, computed, watch } from 'vue'; |
| | | import { EDeviceTypeName, EHmsLevel } from '@/types/enums' |
| | | import { getRoot } from '../../root' |
| | | import { startLivestream, getLiveVideoUrl } from '@/api/manage' |
| | | import { CURRENT_CONFIG as config } from '@/api/http/config' |
| | | // import videojs from 'video.js' |
| | | // import 'video.js/dist/video-js.css'; |
| | | import { getLnglatDist, cartesian3Convert, getLnglatAltitude, createCircleBillboard } from '@/utils/cesium/mapUtil' |
| | | import cesiumOperation from '@/utils/cesium-tsa.js'; |
| | | const { _init, viewerDestory } = cesiumOperation(); |
| | | import * as Cesium from 'cesium'; |
| | | |
| | | // import { cesiumOperation } from '@/hooks/use-cesium-tsa' |
| | | // const { _init } = cesiumOperation(); |
| | | import { useMyStore } from '@/store' |
| | | import { getWaylineFile } from '@/api/manage' |
| | | import comPass from './components/comPass.vue' |
| | | import ptzControl from './components/ptzControl.vue' |
| | | import controlConsole from './components/controlConsole.vue' |
| | | import { useConnectWebSocket } from '@/utils/websocket/connect-websocket' |
| | | import { getWebsocketUrl } from '@/websocket/util/config' |
| | | import { EBizCode, ELocalStorageKey, ERouterName } from '@/types' |
| | | import VConsole from 'vconsole'; |
| | | import { any } from 'video.js/dist/types/utils/events'; |
| | | import { analyzeKmzFile, XMLToJSON } from '@/utils/cesium/kmz.js' |
| | | import { initPointWayLine } from './hooks/initPointWayline' |
| | | import { initPlanarWayline } from './hooks/initPlanarWayline' |
| | | import { droneFly } from './hooks/droneFly' |
| | | import flvjs from 'flv.js'; |
| | | const { pointWayline } = initPointWayLine() |
| | | const { planarWayline } = initPlanarWayline() |
| | | const { initDock } = droneFly() |
| | | |
| | | const { |
| | | removeAllPoint, |
| | | removeAllDataSource, |
| | | globalCesium, |
| | | getEntityById, |
| | | removeById, |
| | | loadLAYER, |
| | | } = cesiumOperation() |
| | | |
| | | // const vConsole = new VConsole(); |
| | | |
| | | let globalViewer = null |
| | | |
| | | // 使用 ref 来存储屏幕宽度和高度 |
| | | const screenWidth = ref(window.innerWidth); |
| | | const screenHeight = ref(window.innerHeight); |
| | | // 地图切换 |
| | | const screenWidthMap = ref(window.innerWidth); |
| | | const screenHeightMap = ref((window.innerHeight/2)+40); |
| | | // 视频切换 |
| | | const screenWidthVideo = ref(window.innerWidth); |
| | | const screenHeightVideo = ref(window.innerHeight/2); |
| | | const isBigMap = ref(false) |
| | | |
| | | // 更新屏幕尺寸的函数 |
| | | const updateScreenSize = () => { |
| | | screenWidth.value = window.innerWidth; |
| | | screenHeight.value = window.innerHeight; |
| | | if (isLandscape.value) { //横屏 |
| | | screenWidthMap.value = window.innerWidth/2 |
| | | screenHeightMap.value = window.innerHeight |
| | | screenWidthVideo.value = window.innerWidth/2 |
| | | screenHeightVideo.value = window.innerHeight |
| | | } else {//竖屏 |
| | | screenWidthMap.value = window.innerWidth |
| | | screenHeightMap.value = (window.innerHeight/2)-40 |
| | | screenWidthVideo.value = window.innerWidth |
| | | screenHeightVideo.value = (window.innerHeight/2)+40 |
| | | } |
| | | }; |
| | | |
| | | const store = useMyStore() |
| | | // 获取路由 |
| | | const root = getRoot() |
| | | |
| | | interface deviceInfo { |
| | | dockSn: string; |
| | | latitude?:number;// 机场经纬度 |
| | | longitude?:number; |
| | | height?:number; // 机场高度 |
| | | isOnline: boolean; |
| | | name: string; // 可选字段 |
| | | sn: string; |
| | | workspaceId:string; |
| | | children?: { }; |
| | | } |
| | | // 获取机场/遥控器sn |
| | | let localData = ref<any>(window.localStorage.getItem('sbInfo')) |
| | | const sbInfo: deviceInfo = JSON.parse(localData.value) as deviceInfo; |
| | | // console.log('哒哒哒', sbInfo) |
| | | // 无人机sn |
| | | const sn = ref<String>(sbInfo.sn) |
| | | // 机场sn |
| | | const dockSn = ref<String>(sbInfo.dockSn) |
| | | |
| | | // 记录横竖屏模式 |
| | | const isLandscape = ref(false); |
| | | |
| | | // 记录视频地址 |
| | | const videoUrl = ref<String>(''); |
| | | |
| | | // 设置图片地址 |
| | | const imageUrl = new URL('@/assets/images/norecord.png', import.meta.url).href |
| | | |
| | | const imageMapSrc = new URL('@/assets/images/mapdock.png', import.meta.url).href |
| | | |
| | | const mapswitching = new URL('@/assets/images/mapswitching.png', import.meta.url).href |
| | | |
| | | const videoWH = ref<any>(null) |
| | | |
| | | // 视频宽高 |
| | | const drawCanvasWidth = ref<Number>(0) |
| | | const drawCanvasHeight = ref<Number>(0) |
| | | |
| | | // 鹰眼视频 |
| | | const timer = null; |
| | | const videoPlayer = ref<any>(null); // 视频播放器实例 |
| | | let webrtcPlayer = null; // video.js 播放器 |
| | | |
| | | // 大屏幕视频 |
| | | const timerBig = null; |
| | | const videoPlayerBig = ref<any>(null); // 视频播放器实例 |
| | | let webrtcPlayerBig = null |
| | | |
| | | let player = null; // video.js 播放器 |
| | | |
| | | // 无人机实时飞行链接 |
| | | let ssLinePath = ref(null) |
| | | |
| | | // 实时定位 |
| | | let viewDroneInfo = {} |
| | | // 机场高度 |
| | | let droneHeight = ref(0) |
| | | |
| | | // 监听 |
| | | let connectWs = ref<any>(null) |
| | | |
| | | let workspaceId = ref<String>(sbInfo.workspaceId) |
| | | localStorage.setItem('bs_workspace_id', workspaceId.value) |
| | | // store.commit('SET_SELECTED_WORKSPACE_ID', workspaceId.value); |
| | | |
| | | // RTCWEB 加载视频 |
| | | const playBig = () => { |
| | | webrtcPlayerBig = new window.ZLMRTCClient.Endpoint({ |
| | | element: videoPlayerBig.value, // video 标签 |
| | | debug: true, // 是否打印日志 |
| | | zlmsdpUrl: videoUrl.value, //流地址 |
| | | simulecast: false, |
| | | useCamera: false, |
| | | audioEnable: true, |
| | | videoEnable: true, |
| | | recvOnly: true, |
| | | usedatachannel: false, |
| | | }) |
| | | webrtcPlayerBig.on( |
| | | window.ZLMRTCClient.Events.WEBRTC_ICE_CANDIDATE_ERROR, |
| | | (e) => { |
| | | // ICE 协商出错 |
| | | console.error('ICE 协商出错') |
| | | // this.eventcallbacK('ICE ERROR', 'ICE 协商出错') |
| | | }, |
| | | ) |
| | | |
| | | webrtcPlayerBig.on( |
| | | window.ZLMRTCClient.Events.WEBRTC_ON_REMOTE_STREAMS, |
| | | (e) => { |
| | | //获取到了远端流,可以播放 |
| | | console.log('播放成功', e.streams) |
| | | // this.eventcallbacK('playing', '播放成功') |
| | | }, |
| | | ) |
| | | |
| | | webrtcPlayerBig.on( |
| | | window.ZLMRTCClient.Events.WEBRTC_OFFER_ANWSER_EXCHANGE_FAILED, |
| | | (e) => { |
| | | // offer anwser 交换失败 |
| | | console.error('offer anwser 交换失败', e) |
| | | // this.eventcallbacK('OFFER ANSWER ERROR ', 'offer anwser 交换失败') |
| | | if (e.code == -400 && e.msg == '流不存在') { |
| | | console.log('流不存在') |
| | | timerBig = setTimeout(() => { |
| | | webrtcPlayerBig.close() |
| | | playBig() |
| | | }, 100) |
| | | } |
| | | }, |
| | | ) |
| | | |
| | | webrtcPlayerBig.on( |
| | | window.ZLMRTCClient.Events.WEBRTC_ON_LOCAL_STREAM, |
| | | (s) => { |
| | | // 获取到了本地流 |
| | | |
| | | // document.getElementById('selfVideo').srcObject=s; |
| | | // this.eventcallbacK('LOCAL STREAM', '获取到了本地流') |
| | | }, |
| | | ) |
| | | } |
| | | // const play = () => { |
| | | // webrtcPlayer = new window.ZLMRTCClient.Endpoint({ |
| | | // element: videoPlayer.value, // video 标签 |
| | | // debug: true, // 是否打印日志 |
| | | // zlmsdpUrl: videoUrl.value, //Rtc地址 |
| | | // simulecast: false, |
| | | // useCamera: false, |
| | | // audioEnable: true, |
| | | // videoEnable: true, |
| | | // recvOnly: true, |
| | | // usedatachannel: false, |
| | | // }) |
| | | // webrtcPlayer.on( |
| | | // window.ZLMRTCClient.Events.WEBRTC_ICE_CANDIDATE_ERROR, |
| | | // (e) => { |
| | | // // ICE 协商出错 |
| | | // console.error('ICE 协商出错') |
| | | // // this.eventcallbacK('ICE ERROR', 'ICE 协商出错') |
| | | // }, |
| | | // ) |
| | | |
| | | // webrtcPlayer.on( |
| | | // window.ZLMRTCClient.Events.WEBRTC_ON_REMOTE_STREAMS, |
| | | // (e) => { |
| | | // //获取到了远端流,可以播放 |
| | | // console.log('播放成功', e.streams) |
| | | // // this.eventcallbacK('playing', '播放成功') |
| | | // }, |
| | | // ) |
| | | |
| | | // webrtcPlayer.on( |
| | | // window.ZLMRTCClient.Events.WEBRTC_OFFER_ANWSER_EXCHANGE_FAILED, |
| | | // (e) => { |
| | | // // offer anwser 交换失败 |
| | | // console.error('offer anwser 交换失败', e) |
| | | // // this.eventcallbacK('OFFER ANSWER ERROR ', 'offer anwser 交换失败') |
| | | // if (e.code == -400 && e.msg == '流不存在') { |
| | | // console.log('流不存在') |
| | | // timer = setTimeout(() => { |
| | | // webrtcPlayer.close() |
| | | // play() |
| | | // }, 100) |
| | | // } |
| | | // }, |
| | | // ) |
| | | |
| | | // webrtcPlayer.on( |
| | | // window.ZLMRTCClient.Events.WEBRTC_ON_LOCAL_STREAM, |
| | | // (s) => { |
| | | // }, |
| | | // ) |
| | | // } |
| | | const videoUrlFlv = ref<String>('') |
| | | // 加载该设备的视频信息 |
| | | const loadVideo = async () => { |
| | | await getLiveVideoUrl(sn.value).then((res) => { |
| | | if (res.data.code != 0) return |
| | | // 获取高度 |
| | | if (videoWH.value) { |
| | | drawCanvasWidth.value = videoWH.value.offsetWidth; |
| | | drawCanvasHeight.value = videoWH.value.offsetHeight; |
| | | } |
| | | videoUrl.value = res.data.data.rtcs_url |
| | | videoUrlFlv.value = res.data.data.url |
| | | // 播放 |
| | | // play() |
| | | playBig() |
| | | // flv |
| | | // initFlvPlayer() |
| | | }) |
| | | } |
| | | |
| | | // flv |
| | | const videoPlayerFlv = ref(null); // 引用 video 元素 |
| | | let flvPlayer = null; // 用于存储 flv.js 播放器实例 |
| | | // 初始化 flv.js 播放器 |
| | | |
| | | const initFlvPlayer = () => { |
| | | if (flvjs.isSupported()) { |
| | | const videoElement = videoPlayerFlv.value; // 获取 video 元素 |
| | | flvPlayer = flvjs.createPlayer({ |
| | | type: 'flv', // 视频格式 |
| | | url: videoUrlFlv.value, // FLV 视频地址 |
| | | }); |
| | | flvPlayer.attachMediaElement(videoElement); // 绑定 video 元素 |
| | | flvPlayer.load(); // 加载视频 |
| | | flvPlayer.play(); // 播放视频 |
| | | } else { |
| | | console.error('Your browser does not support flv.js.'); |
| | | } |
| | | }; |
| | | |
| | | //=============================== |
| | | |
| | | // 监听ws |
| | | const messageHandler = async (payload: any) => { |
| | | // if (!payload) { |
| | | // return |
| | | // } |
| | | switch (payload.biz_code) { |
| | | case EBizCode.GatewayOsd: { // 遥控器 |
| | | store.commit('SET_GATEWAY_INFO', payload.data) |
| | | break |
| | | } |
| | | case EBizCode.DeviceOsd: { |
| | | // console.log('低调点',payload) |
| | | store.commit('SET_DEVICE_INFO', payload.data) |
| | | store.commit('SET_WS_MESSAGE', payload) |
| | | break |
| | | } |
| | | case EBizCode.DockOsd: { // 机场 |
| | | store.commit('SET_DOCK_INFO', payload.data) |
| | | break |
| | | } |
| | | } |
| | | |
| | | } |
| | | |
| | | const webSorketUrl = getWebsocketUrl() + '&workspace-id=' + workspaceId.value |
| | | // 监听ws 消息 |
| | | useConnectWebSocket(messageHandler, webSorketUrl) |
| | | |
| | | |
| | | let savedMapState = null; // 保存地图状态 |
| | | // 销毁之前先保存状态 |
| | | const destroyViewer = () => { |
| | | if ( window.cesiumViewer) { |
| | | // 保存当前地图状态(如相机位置) |
| | | // savedMapState = window.cesiumViewer.camera.view; |
| | | window.cesiumViewer.destroy(); |
| | | window.cesiumViewer = null; |
| | | } |
| | | } |
| | | |
| | | // 地图全屏切换 |
| | | const changeModelMap = () => { |
| | | isBigMap.value = !isBigMap.value |
| | | const cesiumContainer = document.getElementById('cesiumContainerBigMap'); |
| | | const lZp = document.getElementById('lZp'); |
| | | const lZoom = document.getElementById('lZoom'); |
| | | |
| | | if (isBigMap.value) { // 全屏 |
| | | cesiumContainer.style.position = 'fixed'; |
| | | cesiumContainer.style.top = '0'; |
| | | cesiumContainer.style.left = '0'; |
| | | cesiumContainer.style.width = '100%'; |
| | | cesiumContainer.style.height = '100%'; |
| | | lZp.style.right = '40%'; |
| | | lZoom.style.left = '0'; |
| | | } else { // 缩放 |
| | | cesiumContainer.style.position = 'static'; |
| | | cesiumContainer.style.width = `${screenWidthMap.value}` |
| | | cesiumContainer.style.height = `${screenHeight.value}` |
| | | lZp.style.right = '16%'; |
| | | lZoom.style.left = 'auto'; |
| | | |
| | | } |
| | | } |
| | | |
| | | const checkOrientation = async () => { |
| | | const landscapeBox = document.getElementById('landscapeBox'); |
| | | const cesiumContainer = document.getElementById('cesiumContainerBigMap'); |
| | | const videoModule = document.getElementById('videoModule'); |
| | | const lZp = document.getElementById('lZp'); |
| | | const lZoom = document.getElementById('lZoom'); |
| | | // 横屏再加载视频请求 |
| | | // destroyViewer() |
| | | |
| | | // loadVideo() |
| | | if (window.innerWidth > window.innerHeight) { |
| | | isLandscape.value = true; |
| | | updateScreenSize() |
| | | landscapeBox.style.display = 'flex'; |
| | | lZp.style.right = '16%'; |
| | | lZoom.style.display = 'block'; |
| | | lZoom.style.position = 'fixed'; |
| | | // await nextTick(); |
| | | // await _init('cesiumContainerBigMap'); |
| | | // await getWrjSsLx(); |
| | | } else { |
| | | console.log('是否进来',landscapeBox) |
| | | isLandscape.value = false; |
| | | updateScreenSize() |
| | | landscapeBox.style.display = 'inherit'; |
| | | lZp.style.right = '28%'; |
| | | lZoom.style.display = 'none'; |
| | | // landscapeBox.style.width = `${screenWidth}` |
| | | // landscapeBox.style.height = `${screenHeight}` |
| | | // videoModule.style.width = `${screenWidth}` |
| | | // videoModule.style.height = `${screenHeight}` |
| | | // await nextTick(); |
| | | // await _init('cesiumContainerMap'); |
| | | // await getWrjSsLx(); |
| | | } |
| | | }; |
| | | |
| | | // 获取实时航线 |
| | | const getWrjSsLx = () => { |
| | | getWaylineFile(sbInfo.sn).then((res:any) => { |
| | | if (res.code != 200) return |
| | | ssLinePath.value = res.data |
| | | globalViewer = window.cesiumViewer |
| | | // 先有航线,再飞行 |
| | | generateCourse() |
| | | }) |
| | | } |
| | | |
| | | // 切换航线轨迹 |
| | | const courseTrack = (data:any) => { |
| | | if (window.cesiumViewer== null) return |
| | | const currentSn = dockSn.value |
| | | const longitude = data.dockInfo[currentSn]?.basic_osd?.longitude || null |
| | | const latitude = data.dockInfo[currentSn]?.basic_osd?.latitude || null |
| | | const height = data.dockInfo[currentSn]?.basic_osd?.height || null |
| | | let getLongOk = ref(0) |
| | | if (!getEntityById('drone_dock')) { |
| | | getLnglatAltitude(longitude, latitude, window.cesiumViewer).then(res => { |
| | | removeById('drone_dock') |
| | | window.cesiumViewer.entities.add({ |
| | | position: globalCesium.Cartesian3.fromDegrees( |
| | | longitude, |
| | | latitude, |
| | | res.height, |
| | | ), |
| | | id: 'drone_dock', |
| | | billboard: { |
| | | image: imageMapSrc, |
| | | outlineWidth: 0, |
| | | width: 36, |
| | | height: 36, |
| | | scale: 1.0, |
| | | } |
| | | }) |
| | | window.cesiumViewer.scene.camera.setView({ |
| | | destination: globalCesium.Cartesian3.fromDegrees( |
| | | Number(longitude), |
| | | Number(latitude), |
| | | 10000.0, |
| | | ), |
| | | }) |
| | | droneHeight.value = res.height |
| | | }) |
| | | } else { |
| | | // 当机场坐标存在时,判断获取的位置与机场坐标位置是否一致,若不是,重新更新位置 |
| | | let dornePoint = cartesian3Convert( |
| | | getEntityById('drone_dock')._position._value, |
| | | window.cesiumViewer, |
| | | ) |
| | | getLongOk = getLnglatDist( |
| | | dornePoint.longitude, |
| | | dornePoint.latitude, |
| | | longitude, |
| | | latitude, |
| | | ) |
| | | if (getLongOk.value > 100) { |
| | | removeById('drone_dock') |
| | | } |
| | | } |
| | | if (getLongOk.value > 100) return |
| | | initDock(data, sn.value, dockSn.value, workspaceId.value, ssLinePath.value, viewDroneInfo) |
| | | } |
| | | |
| | | // 监听 |
| | | watch(() => store.state.deviceState, (newValue, oldValue) => { |
| | | // 试试 |
| | | if (newValue) { |
| | | nextTick() |
| | | courseTrack(newValue) |
| | | } |
| | | }, {deep: true}) |
| | | |
| | | watch(() => store.state.wsMessage, (newValue, oldValue) => { |
| | | viewDroneInfo.longitude = newValue?.longitude |
| | | viewDroneInfo.latitude = newValue?.latitude |
| | | viewDroneInfo.elevation = Number(newValue?.elevation.toFixed(2)) + droneHeight.value |
| | | }, {deep: true}) |
| | | |
| | | |
| | | |
| | | // 生成航线轨迹 |
| | | const generateCourse = async () => { |
| | | if (!ssLinePath.value) return |
| | | const { fileInfoObj } = await analyzeKmzFile(`${ssLinePath.value}?_t=${new Date().getTime()}`) |
| | | const xmlStr = await fileInfoObj['wpmz/template.kml'] |
| | | const xmlJson = XMLToJSON(xmlStr)?.['Document'] |
| | | const placemark = xmlJson.Folder.Placemark |
| | | if (placemark?.Polygon) { |
| | | // return 'planar' |
| | | planarWayline(ssLinePath.value, sbInfo.longitude, sbInfo.latitude) |
| | | } else { |
| | | // return 'point' |
| | | pointWayline(ssLinePath.value, sbInfo.longitude, sbInfo.latitude) |
| | | } |
| | | } |
| | | |
| | | onMounted(async () => { |
| | | window.addEventListener('orientationchange', checkOrientation); |
| | | window.addEventListener('resize', checkOrientation); // 兼容某些设备 |
| | | checkOrientation(); // 初始化时检查一次 |
| | | loadVideo() |
| | | await nextTick(); |
| | | await _init('cesiumContainerBigMap'); |
| | | await getWrjSsLx(); |
| | | |
| | | }); |
| | | |
| | | onBeforeUnmount(() => { |
| | | window.removeEventListener('orientationchange', checkOrientation); |
| | | window.removeEventListener('resize', checkOrientation); |
| | | if (player) { |
| | | player.dispose(); |
| | | } |
| | | if (flvPlayer) { |
| | | flvPlayer.pause(); // 暂停播放 |
| | | flvPlayer.unload(); // 卸载视频 |
| | | flvPlayer.detachMediaElement(); // 解绑 video 元素 |
| | | flvPlayer.destroy(); // 销毁播放器 |
| | | flvPlayer = null; |
| | | } |
| | | }); |
| | | |
| | | |
| | | |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .drone-pilot-details { |
| | | .landscape-box { |
| | | position: relative; |
| | | display: flex; |
| | | .l-map { |
| | | .l-zp { |
| | | position: fixed; |
| | | right: 16%; |
| | | bottom: 0; |
| | | } |
| | | .l-zoom { |
| | | position: fixed; |
| | | bottom: 0; |
| | | .zoom { |
| | | width: 2rem; |
| | | height: 2rem; |
| | | border-radius: 3px; |
| | | background-color: rgba(0, 0, 0, 0.8); |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | overflow: hidden; |
| | | cursor: pointer; |
| | | pointer-events: all; |
| | | img { |
| | | width: 1.6rem; |
| | | height: 1.6rem; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | .l-video { |
| | | .el-empty { |
| | | width: 100%; |
| | | height: 100%; |
| | | padding: 0; |
| | | ::v-deep(.el-empty__image) { |
| | | width: 50%; |
| | | height: 50%; |
| | | } |
| | | ::v-deep(.el-empty__description) { |
| | | margin: 0; |
| | | } |
| | | } |
| | | } |
| | | #cesiumContainerBigMap { |
| | | width: 100%; |
| | | height: 100%; |
| | | overflow: hidden; |
| | | |
| | | :deep() { |
| | | .cesium-viewer { |
| | | width: 100%; |
| | | height: 100%; |
| | | |
| | | .cesium-viewer-cesiumWidgetContainer { |
| | | width: 100%; |
| | | height: 100%; |
| | | |
| | | .cesium-widget { |
| | | width: 100%; |
| | | height: 100%; |
| | | |
| | | canvas { |
| | | width: 100%; |
| | | height: 100%; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | #cesiumContainerMap { |
| | | width: 100%; |
| | | height: 100%; |
| | | overflow: hidden; |
| | | |
| | | :deep() { |
| | | .cesium-viewer { |
| | | width: 100%; |
| | | height: 100%; |
| | | |
| | | .cesium-viewer-cesiumWidgetContainer { |
| | | width: 100%; |
| | | height: 100%; |
| | | |
| | | .cesium-widget { |
| | | width: 100%; |
| | | height: 100%; |
| | | |
| | | canvas { |
| | | width: 100%; |
| | | height: 100%; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | .portrait-screen { |
| | | .l-map { |
| | | .l-zp { |
| | | position: fixed; |
| | | right: 28%; |
| | | bottom: 0; |
| | | } |
| | | .l-zoom { |
| | | position: fixed; |
| | | bottom: 0; |
| | | .zoom { |
| | | width: 2rem; |
| | | height: 2rem; |
| | | border-radius: 3px; |
| | | background-color: rgba(0, 0, 0, 0.8); |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | overflow: hidden; |
| | | cursor: pointer; |
| | | pointer-events: all; |
| | | img { |
| | | width: 1.6rem; |
| | | height: 1.6rem; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | </style> |
| New file |
| | |
| | | <template> |
| | | <div class="drone-pilot-details" :style="{ width: screenWidth,height: screenHeight}"> |
| | | <div id="landscapeBox" class="landscape-box" :style="{ width: screenWidth + 'px', height: (screenHeight) + 'px'}"> |
| | | <div id="videoModule" class="l-video" :style="{ width: screenWidthVideo + 'px', height: (screenHeightVideo) + 'px'}"> |
| | | <video v-show="videoUrl" ref="videoPlayerBig" :style="{ width: screenWidthVideo + 'px', height: screenHeightVideo + 'px'}" controls autoplay muted playsinline style="text-align: left; object-fit: fill"> |
| | | Your browser is too old which doesn't support HTML5 video. |
| | | </video> |
| | | <el-empty v-show="videoUrl == ''" description="当前设备已关机,无法进行直播" :image="imageUrl"></el-empty> |
| | | </div> |
| | | <div class="l-map" :style="{ width: screenWidthVideo + 'px', height: (screenHeightVideo) + 'px'}"> |
| | | <div id="cesiumContainerBigMap"></div> |
| | | <div class="l-zp" id="lZp"> |
| | | <comPass :cesiumViewe="globalViewer"/> |
| | | </div> |
| | | <div class="l-zoom" id="lZoom"> |
| | | <div class="zoom"> |
| | | <img src="@/assets/images/me.png" @touchstart="getLocation" /> |
| | | </div> |
| | | <div class="zoom" @touchstart="onChangeD"> |
| | | <img v-if="is2d" src="@/assets/images/stand.png" /> |
| | | <img v-else src="@/assets/images/satellite.png" /> |
| | | </div> |
| | | <div class="zoom"> |
| | | <img v-if="!isBigMap" src="@/assets/images/fang.png" @touchstart="changeModelMap" /> |
| | | <img v-else src="@/assets/images/suo.png" @touchstart="changeModelMap" /> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <div class="right-box"> |
| | | <ptzControl :sn="sn" :osdVisible="sbInfo" /> |
| | | <controlConsole :sn="sn" :osdVisible="sbInfo" :cesiumViewe="globalViewer" /> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script lang="ts" setup> |
| | | import { ref, onMounted, onBeforeUnmount, nextTick, reactive, computed, watch } from 'vue'; |
| | | import { EDeviceTypeName, EHmsLevel } from '@/types/enums' |
| | | import { getRoot } from '../../root' |
| | | import { startLivestream, getLiveVideoUrl } from '@/api/manage' |
| | | import { CURRENT_CONFIG as config } from '@/api/http/config' |
| | | // import videojs from 'video.js' |
| | | // import 'video.js/dist/video-js.css'; |
| | | import { getLnglatDist, cartesian3Convert, getLnglatAltitude, createCircleBillboard } from '@/utils/cesium/mapUtil' |
| | | import cesiumOperation from '@/utils/cesium-tsa.js'; |
| | | const { _init, viewerDestory } = cesiumOperation(); |
| | | import * as Cesium from 'cesium'; |
| | | |
| | | // import { cesiumOperation } from '@/hooks/use-cesium-tsa' |
| | | // const { _init } = cesiumOperation(); |
| | | import { useMyStore } from '@/store' |
| | | import { getWaylineFile } from '@/api/manage' |
| | | import comPass from './components/comPass.vue' |
| | | import ptzControl from './components/ptzControl.vue' |
| | | import controlConsole from './components/controlConsole.vue' |
| | | import { useConnectWebSocket } from '@/utils/websocket/connect-websocket' |
| | | import { getWebsocketUrl } from '@/websocket/util/config' |
| | | import { EBizCode, ELocalStorageKey, ERouterName } from '@/types' |
| | | import VConsole from 'vconsole'; |
| | | import { any } from 'video.js/dist/types/utils/events'; |
| | | import { analyzeKmzFile, XMLToJSON } from '@/utils/cesium/kmz.js' |
| | | import { initPointWayLine } from './hooks/initPointWayline' |
| | | import { initPlanarWayline } from './hooks/initPlanarWayline' |
| | | import { droneFly } from './hooks/droneFly' |
| | | import flvjs from 'flv.js'; |
| | | const { pointWayline } = initPointWayLine() |
| | | const { planarWayline } = initPlanarWayline() |
| | | const { initDock } = droneFly() |
| | | |
| | | const { |
| | | removeAllPoint, |
| | | removeAllDataSource, |
| | | globalCesium, |
| | | getEntityById, |
| | | removeById, |
| | | loadLAYER, |
| | | } = cesiumOperation() |
| | | |
| | | const vConsole = new VConsole(); |
| | | |
| | | let globalViewer = null |
| | | |
| | | // 使用 ref 来存储屏幕宽度和高度 |
| | | const screenWidth = ref(window.innerWidth); |
| | | const screenHeight = ref(window.innerHeight); |
| | | // 地图切换 |
| | | const screenWidthMap = ref((window.innerHeight/2)*2)// ref(window.innerWidth); |
| | | const screenHeightMap = ref((window.innerHeight/2)); |
| | | // 视频切换 |
| | | const screenWidthVideo = ref(window.innerWidth); |
| | | const screenHeightVideo = ref(window.innerHeight/2); |
| | | const isBigMap = ref(false) |
| | | |
| | | // 显示高德地图矢量还是影像(2D/3D) |
| | | let is2d = ref(true) |
| | | |
| | | const onChangeD = () => { |
| | | is2d.value = !is2d.value |
| | | store.commit('SET_MAP_SETTING_MODE', is2d.value?2:3); |
| | | loadLAYER(); |
| | | } |
| | | |
| | | const store = useMyStore() |
| | | // 获取路由 |
| | | const root = getRoot() |
| | | |
| | | interface deviceInfo { |
| | | dockSn: string; |
| | | latitude?:number;// 机场经纬度 |
| | | longitude?:number; |
| | | height?:number; // 机场高度 |
| | | isOnline: boolean; |
| | | name: string; // 可选字段 |
| | | sn: string; |
| | | workspaceId:string; |
| | | children?: { }; |
| | | } |
| | | // 获取机场/遥控器sn |
| | | let localData = ref<any>(window.localStorage.getItem('sbInfo')) |
| | | const sbInfo: deviceInfo = JSON.parse(localData.value) as deviceInfo; |
| | | |
| | | // 无人机sn |
| | | const sn = ref<String>(sbInfo.sn) |
| | | // 机场sn |
| | | const dockSn = ref<String>(sbInfo.dockSn) |
| | | |
| | | // 记录横竖屏模式 |
| | | const isLandscape = ref(false); |
| | | |
| | | // 记录视频地址 |
| | | const videoUrl = ref<String>(''); |
| | | |
| | | // 设置图片地址 |
| | | const imageUrl = new URL('@/assets/images/norecord.png', import.meta.url).href |
| | | |
| | | const imageMapSrc = new URL('@/assets/images/mapdock.png', import.meta.url).href |
| | | |
| | | const meMapSrc = new URL('@/assets/images/me.png', import.meta.url).href |
| | | |
| | | const jgbMapSrc = new URL('@/assets/images/jgb.png', import.meta.url).href |
| | | |
| | | const mapswitching = new URL('@/assets/images/mapswitching.png', import.meta.url).href |
| | | |
| | | const videoWH = ref<any>(null) |
| | | |
| | | // 视频宽高 |
| | | const drawCanvasWidth = ref<Number>(0) |
| | | const drawCanvasHeight = ref<Number>(0) |
| | | |
| | | // 鹰眼视频 |
| | | const timer = null; |
| | | const videoPlayer = ref<any>(null); // 视频播放器实例 |
| | | let webrtcPlayer = null; // video.js 播放器 |
| | | |
| | | // 大屏幕视频 |
| | | const timerBig = null; |
| | | const videoPlayerBig = ref<any>(null); // 视频播放器实例 |
| | | let webrtcPlayerBig = null |
| | | |
| | | let player = null; // video.js 播放器 |
| | | |
| | | // 无人机实时飞行链接 |
| | | let ssLinePath = ref(null) |
| | | |
| | | // 实时定位 |
| | | let viewDroneInfo = {} |
| | | // 机场高度 |
| | | let droneHeight = ref(0) |
| | | |
| | | // 监听 |
| | | let connectWs = ref<any>(null) |
| | | |
| | | let workspaceId = ref<String>(sbInfo.workspaceId) |
| | | localStorage.setItem('bs_workspace_id', workspaceId.value) |
| | | // store.commit('SET_SELECTED_WORKSPACE_ID', workspaceId.value); |
| | | |
| | | // 自身定位 |
| | | let me_latitude = 0; |
| | | let me_longitude = 0; |
| | | |
| | | // RTCWEB 加载视频 |
| | | const playBig = () => { |
| | | webrtcPlayerBig = new window.ZLMRTCClient.Endpoint({ |
| | | element: videoPlayerBig.value, // video 标签 |
| | | debug: true, // 是否打印日志 |
| | | zlmsdpUrl: videoUrl.value, //流地址 |
| | | simulecast: false, |
| | | useCamera: false, |
| | | audioEnable: true, |
| | | videoEnable: true, |
| | | recvOnly: true, |
| | | usedatachannel: false, |
| | | }) |
| | | webrtcPlayerBig.on( |
| | | window.ZLMRTCClient.Events.WEBRTC_ICE_CANDIDATE_ERROR, |
| | | (e) => { |
| | | // ICE 协商出错 |
| | | console.error('ICE 协商出错') |
| | | // this.eventcallbacK('ICE ERROR', 'ICE 协商出错') |
| | | }, |
| | | ) |
| | | |
| | | webrtcPlayerBig.on( |
| | | window.ZLMRTCClient.Events.WEBRTC_ON_REMOTE_STREAMS, |
| | | (e) => { |
| | | //获取到了远端流,可以播放 |
| | | console.log('播放成功', e.streams) |
| | | // this.eventcallbacK('playing', '播放成功') |
| | | }, |
| | | ) |
| | | |
| | | webrtcPlayerBig.on( |
| | | window.ZLMRTCClient.Events.WEBRTC_OFFER_ANWSER_EXCHANGE_FAILED, |
| | | (e) => { |
| | | // offer anwser 交换失败 |
| | | console.error('offer anwser 交换失败', e) |
| | | // this.eventcallbacK('OFFER ANSWER ERROR ', 'offer anwser 交换失败') |
| | | if (e.code == -400 && e.msg == '流不存在') { |
| | | console.log('流不存在') |
| | | timerBig = setTimeout(() => { |
| | | webrtcPlayerBig.close() |
| | | playBig() |
| | | }, 100) |
| | | } |
| | | }, |
| | | ) |
| | | |
| | | webrtcPlayerBig.on( |
| | | window.ZLMRTCClient.Events.WEBRTC_ON_LOCAL_STREAM, |
| | | (s) => { |
| | | // 获取到了本地流 |
| | | |
| | | // document.getElementById('selfVideo').srcObject=s; |
| | | // this.eventcallbacK('LOCAL STREAM', '获取到了本地流') |
| | | }, |
| | | ) |
| | | } |
| | | |
| | | |
| | | // 加载该设备的视频信息 |
| | | const loadVideo = async () => { |
| | | await getLiveVideoUrl(sn.value).then((res) => { |
| | | if (res.data.code != 0) return |
| | | // 获取高度 |
| | | if (videoWH.value) { |
| | | drawCanvasWidth.value = videoWH.value.offsetWidth; |
| | | drawCanvasHeight.value = videoWH.value.offsetHeight; |
| | | } |
| | | videoUrl.value = res.data.data.rtcs_url |
| | | // 播放 |
| | | playBig() |
| | | }) |
| | | } |
| | | |
| | | //=============================== |
| | | |
| | | // 监听ws |
| | | const messageHandler = async (payload: any) => { |
| | | // if (!payload) { |
| | | // return |
| | | // } |
| | | switch (payload.biz_code) { |
| | | case EBizCode.GatewayOsd: { // 遥控器 |
| | | store.commit('SET_GATEWAY_INFO', payload.data) |
| | | break |
| | | } |
| | | case EBizCode.DeviceOsd: { |
| | | store.commit('SET_DEVICE_INFO', payload.data) |
| | | store.commit('SET_WS_MESSAGE', payload) |
| | | break |
| | | } |
| | | case EBizCode.DockOsd: { // 机场 |
| | | store.commit('SET_DOCK_INFO', payload.data) |
| | | break |
| | | } |
| | | } |
| | | |
| | | } |
| | | |
| | | const webSorketUrl = getWebsocketUrl() + '&workspace-id=' + workspaceId.value |
| | | // 监听ws 消息 |
| | | useConnectWebSocket(messageHandler, webSorketUrl) |
| | | |
| | | |
| | | let savedMapState = null; // 保存地图状态 |
| | | // 销毁之前先保存状态 |
| | | const destroyViewer = () => { |
| | | if ( window.cesiumViewer) { |
| | | // 保存当前地图状态(如相机位置) |
| | | // savedMapState = window.cesiumViewer.camera.view; |
| | | window.cesiumViewer.destroy(); |
| | | window.cesiumViewer = null; |
| | | } |
| | | } |
| | | |
| | | // 地图全屏切换 |
| | | const changeModelMap = () => { |
| | | isBigMap.value = !isBigMap.value |
| | | const cesiumContainer = document.getElementById('cesiumContainerBigMap'); |
| | | const lZp = document.getElementById('lZp'); |
| | | // const lZoom = document.getElementById('lZoom'); |
| | | if (isLandscape.value) { // 横屏 |
| | | if (isBigMap.value) { // 全屏 |
| | | cesiumContainer.style.position = 'fixed'; |
| | | cesiumContainer.style.top = '0'; |
| | | cesiumContainer.style.left = '0'; |
| | | cesiumContainer.style.width = `${screenWidthMap.value * 2}px` |
| | | cesiumContainer.style.height = `${screenHeightMap.value}px` |
| | | lZp.style.right = '40%'; |
| | | // lZoom.style.left = '0'; |
| | | } else { // 缩放 |
| | | cesiumContainer.style.width = `${screenWidthMap.value}px` |
| | | cesiumContainer.style.height = `${screenHeightMap.value}px` |
| | | cesiumContainer.style.position = 'static'; |
| | | lZp.style.right = '16%'; |
| | | // lZoom.style.left = '50%'; |
| | | |
| | | } |
| | | } else { |
| | | if (isBigMap.value) { // 全屏 |
| | | cesiumContainer.style.position = 'fixed'; |
| | | cesiumContainer.style.top = '0'; |
| | | cesiumContainer.style.left = '0'; |
| | | cesiumContainer.style.width = `${screenWidthMap.value}px` |
| | | cesiumContainer.style.height = `${screenHeightMap.value * 2}px` |
| | | lZp.style.right = '30%'; |
| | | // lZoom.style.right = '0'; |
| | | } else { // 缩放 |
| | | console.log('竖屏宽度',screenWidthMap.value) |
| | | console.log('竖屏高度度',screenHeightMap.value) |
| | | cesiumContainer.style.width = `${screenWidthMap.value}px` |
| | | cesiumContainer.style.height = `${screenHeightMap.value}px` |
| | | cesiumContainer.style.position = 'static'; |
| | | lZp.style.right = '30%'; |
| | | // lZoom.style.right = '0'; |
| | | |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 更新屏幕尺寸的函数 |
| | | // const updateScreenSize = () => { |
| | | // const cesiumContainer = document.getElementById('cesiumContainerBigMap'); |
| | | // screenWidth.value = window.innerWidth; |
| | | // screenHeight.value = window.innerHeight; |
| | | // console.log('屏幕自身宽度',window.innerWidth) |
| | | // console.log('屏幕自身高度',window.innerHeight) |
| | | // if (isLandscape.value) { //横屏 |
| | | // screenWidthMap.value = window.innerWidth/2 |
| | | // screenHeightMap.value = window.innerHeight |
| | | // screenWidthVideo.value = window.innerWidth/2 |
| | | // screenHeightVideo.value = window.innerHeight |
| | | // cesiumContainer.style.width = `${screenWidthMap.value}px` |
| | | // cesiumContainer.style.height = `${screenHeightMap.value}px` |
| | | // } else {//竖屏 |
| | | // screenWidthMap.value = window.innerHeight |
| | | // screenHeightMap.value = (window.innerHeight/2) |
| | | // screenWidthVideo.value = window.innerWidth |
| | | // screenHeightVideo.value = (window.innerHeight/2) |
| | | // // 重新给赋值宽高 |
| | | // cesiumContainer.style.width = `${screenWidthMap.value}px` |
| | | // cesiumContainer.style.height = `${screenHeightMap.value}px` |
| | | // console.log('切换竖屏宽度',screenWidthMap.value) |
| | | // console.log('切换竖屏高度度',screenHeightMap.value) |
| | | // } |
| | | |
| | | // }; |
| | | |
| | | const checkOrientation = async () => { |
| | | screenWidth.value = window.innerWidth; |
| | | screenHeight.value = window.innerHeight; |
| | | console.log('屏幕宽度', screenWidth.value) |
| | | console.log('屏幕高度', screenHeight.value) |
| | | const landscapeBox = document.getElementById('landscapeBox'); |
| | | let cesiumContainer = document.getElementById('cesiumContainerBigMap'); |
| | | const videoModule = document.getElementById('videoModule'); |
| | | const lZp = document.getElementById('lZp'); |
| | | const lZoom = document.getElementById('lZoom'); |
| | | |
| | | if (window.innerWidth > window.innerHeight) {//横屏 |
| | | screenWidthVideo.value = window.innerWidth/2 |
| | | screenHeightVideo.value = window.innerHeight |
| | | isLandscape.value = true; |
| | | // updateScreenSize() |
| | | landscapeBox.style.display = 'flex'; |
| | | lZp.style.right = '16%'; |
| | | lZoom.style.position = 'fixed'; |
| | | cesiumContainer.style.width = `${screenWidth.value/2}px` |
| | | cesiumContainer.style.height = `${screenHeight.value}px` |
| | | } else { //竖屏 |
| | | isLandscape.value = false; |
| | | screenWidthVideo.value = window.innerWidth |
| | | screenHeightVideo.value = (window.innerHeight/2) |
| | | // updateScreenSize() |
| | | landscapeBox.style.display = 'inherit'; |
| | | landscapeBox.style.overflow = 'hidden'; |
| | | lZp.style.right = '32%'; |
| | | cesiumContainer.style.width = `${screenHeight.value}px` |
| | | cesiumContainer.style.height = `${screenHeight.value/2}px` |
| | | } |
| | | }; |
| | | |
| | | // 获取实时航线 |
| | | const getWrjSsLx = () => { |
| | | globalViewer = window.cesiumViewer; |
| | | // ssLinePath.value = 'https://wrj.shuixiongit.com/minio/cloud-bucket/wayline/20250211/wayline_1739254562374.kmz' |
| | | // generateCourse() |
| | | // return |
| | | getWaylineFile(sbInfo.sn).then((res:any) => { |
| | | if (res.code != 200) return |
| | | ssLinePath.value = res.data |
| | | // globalViewer = window.cesiumViewer |
| | | // 先有航线,再飞行 |
| | | generateCourse() |
| | | }) |
| | | } |
| | | |
| | | // 切换航线轨迹 |
| | | const courseTrack = (data:any) => { |
| | | if (window.cesiumViewer== null) return |
| | | const currentSn = dockSn.value |
| | | const longitude = data.dockInfo[currentSn]?.basic_osd?.longitude || null |
| | | const latitude = data.dockInfo[currentSn]?.basic_osd?.latitude || null |
| | | const height = data.dockInfo[currentSn]?.basic_osd?.height || null |
| | | let getLongOk = ref(0) |
| | | if (!getEntityById('drone_dock')) { |
| | | getLnglatAltitude(longitude, latitude, window.cesiumViewer).then(res => { |
| | | removeById('drone_dock') |
| | | window.cesiumViewer.entities.add({ |
| | | position: globalCesium.Cartesian3.fromDegrees( |
| | | longitude, |
| | | latitude, |
| | | res.height, |
| | | ), |
| | | id: 'drone_dock', |
| | | billboard: { |
| | | image: imageMapSrc, |
| | | outlineWidth: 0, |
| | | width: 36, |
| | | height: 36, |
| | | scale: 1.0, |
| | | } |
| | | }) |
| | | window.cesiumViewer.scene.camera.setView({ |
| | | destination: globalCesium.Cartesian3.fromDegrees( |
| | | Number(longitude), |
| | | Number(latitude), |
| | | 10000.0, |
| | | ), |
| | | }) |
| | | droneHeight.value = res.height |
| | | }) |
| | | } else { |
| | | // 当机场坐标存在时,判断获取的位置与机场坐标位置是否一致,若不是,重新更新位置 |
| | | let dornePoint = cartesian3Convert(getEntityById('drone_dock')._position._value, window.cesiumViewer,) |
| | | getLongOk = getLnglatDist( |
| | | dornePoint.longitude, |
| | | dornePoint.latitude, |
| | | longitude, |
| | | latitude, |
| | | ) |
| | | if (getLongOk.value > 100) { |
| | | removeById('drone_dock') |
| | | } |
| | | } |
| | | if (getLongOk.value > 100) return |
| | | initDock(data, sn.value, dockSn.value, workspaceId.value, ssLinePath.value, viewDroneInfo, me_latitude, me_longitude) |
| | | } |
| | | |
| | | // 监听 |
| | | watch(() => store.state.deviceState, (newValue, oldValue) => { |
| | | // 试试 |
| | | if (newValue) { |
| | | nextTick() |
| | | courseTrack(newValue) |
| | | } |
| | | }, {deep: true}) |
| | | |
| | | watch(() => store.state.wsMessage, (newValue, oldValue) => { |
| | | // 控制台报 Expected longitude to be typeof number, actual typeof was undefined 是因为这里没有及时拿到longitude数据 |
| | | if (newValue) { |
| | | viewDroneInfo.longitude = newValue?.longitude |
| | | viewDroneInfo.latitude = newValue?.latitude |
| | | viewDroneInfo.elevation = Number(newValue?.elevation.toFixed(2)) + droneHeight.value |
| | | } |
| | | }, {deep: true}) |
| | | |
| | | |
| | | |
| | | // 生成航线轨迹 |
| | | const generateCourse = async () => { |
| | | // ssLinePath.value = 'https://wrj.shuixiongit.com/minio/cloud-bucket/wayline/20250211/wayline_1739254562374.kmz' |
| | | if (!ssLinePath.value) return |
| | | const { fileInfoObj } = await analyzeKmzFile(`${ssLinePath.value}?_t=${new Date().getTime()}`) |
| | | const xmlStr = await fileInfoObj['wpmz/template.kml'] |
| | | const xmlJson = XMLToJSON(xmlStr)?.['Document'] |
| | | const placemark = xmlJson.Folder.Placemark |
| | | console.log('看看航线值', sbInfo) |
| | | if (placemark?.Polygon) { |
| | | // return 'planar' |
| | | planarWayline(ssLinePath.value, sbInfo.longitude, sbInfo.latitude) |
| | | } else { |
| | | // return 'point' |
| | | pointWayline(ssLinePath.value, sbInfo.longitude, sbInfo.latitude) |
| | | } |
| | | } |
| | | |
| | | // 获取自身定位 |
| | | const getLocation = () => { |
| | | if (window.cesiumViewer == null) return |
| | | // 获取设备朝向 |
| | | if (window.DeviceOrientationEvent) { |
| | | window.addEventListener('deviceorientation', (event) => { |
| | | const alpha = event.alpha; // 方位角 |
| | | const beta = event.beta; // 前后倾斜 |
| | | const gamma = event.gamma; // 左右倾斜 |
| | | |
| | | if (alpha !== null && beta !== null && gamma !== null) { |
| | | updateCesiumCamera(alpha, beta, gamma); |
| | | } |
| | | }); |
| | | } else { |
| | | console.log('Device Orientation API is not supported.'); |
| | | } |
| | | // if (navigator.geolocation) { |
| | | // navigator.geolocation.getCurrentPosition( |
| | | // (position) => { |
| | | me_latitude = 28.624647955787974;// position.coords.latitude; |
| | | me_longitude = 115.85635808986208;// position.coords.longitude; |
| | | // 开始给自己定位 |
| | | getLnglatAltitude(me_longitude, me_latitude, window.cesiumViewer).then(res => { |
| | | removeById('me_coordinate') |
| | | window.cesiumViewer.entities.add({ |
| | | position: globalCesium.Cartesian3.fromDegrees( |
| | | me_longitude, |
| | | me_latitude, |
| | | res.height || 0, |
| | | ), |
| | | id: 'me_coordinate', |
| | | billboard: { |
| | | image: meMapSrc, |
| | | outlineWidth: 0, |
| | | width: 36, |
| | | height: 36, |
| | | scale: 1.0, |
| | | } |
| | | }) |
| | | window.cesiumViewer.scene.camera.setView({ |
| | | destination: globalCesium.Cartesian3.fromDegrees( |
| | | Number(me_longitude), |
| | | Number(me_latitude), |
| | | 10000.0, |
| | | ), |
| | | }) |
| | | }) |
| | | // }, |
| | | // (err) => { |
| | | // console.log('定位有误') |
| | | // } |
| | | // ); |
| | | // } else { |
| | | // console.log('您的浏览器不支持地理定位') |
| | | // } |
| | | }; |
| | | |
| | | onMounted(async () => { |
| | | window.addEventListener('orientationchange', checkOrientation); |
| | | window.addEventListener('resize', checkOrientation); // 兼容某些设备 |
| | | checkOrientation(); // 初始化时检查一次 |
| | | loadVideo() |
| | | await nextTick(); |
| | | await _init('cesiumContainerBigMap'); |
| | | await getWrjSsLx(); |
| | | // await generateCourse() |
| | | await getLocation() |
| | | |
| | | |
| | | |
| | | }); |
| | | |
| | | onBeforeUnmount(() => { |
| | | window.removeEventListener('orientationchange', checkOrientation); |
| | | window.removeEventListener('resize', checkOrientation); |
| | | if (player) { |
| | | player.dispose(); |
| | | } |
| | | }); |
| | | |
| | | |
| | | |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .drone-pilot-details { |
| | | .landscape-box { |
| | | overflow: hidden; |
| | | // position: relative; |
| | | // display: flex; |
| | | .l-map { |
| | | .l-zp { |
| | | position: fixed; |
| | | right: 16%; |
| | | bottom: 0; |
| | | } |
| | | .l-zoom { |
| | | position: fixed; |
| | | bottom: 0; |
| | | right: 0; |
| | | .zoom { |
| | | width: 2rem; |
| | | height: 2rem; |
| | | margin: 0.2rem 0.4rem; |
| | | border-radius: 3px; |
| | | background-color: rgba(0, 0, 0, 0.8); |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | overflow: hidden; |
| | | cursor: pointer; |
| | | pointer-events: all; |
| | | img { |
| | | width: 1.6rem; |
| | | height: 1.6rem; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | .l-video { |
| | | .el-empty { |
| | | width: 100%; |
| | | height: 100%; |
| | | padding: 0; |
| | | ::v-deep(.el-empty__image) { |
| | | width: 50%; |
| | | height: 50%; |
| | | } |
| | | ::v-deep(.el-empty__description) { |
| | | margin: 0; |
| | | } |
| | | } |
| | | } |
| | | #cesiumContainerBigMap { |
| | | width: 100%; |
| | | height: 100%; |
| | | margin: 0; |
| | | padding: 0; |
| | | overflow: hidden; |
| | | |
| | | :deep() { |
| | | .cesium-viewer { |
| | | width: 100%; |
| | | height: 100%; |
| | | |
| | | .cesium-viewer-cesiumWidgetContainer { |
| | | width: 100%; |
| | | height: 100%; |
| | | |
| | | .cesium-widget { |
| | | width: 100%; |
| | | height: 100%; |
| | | |
| | | canvas { |
| | | width: 100%; |
| | | height: 100%; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | .right-box {} |
| | | } |
| | | </style> |
| | |
| | | <template> |
| | | <div class="drone-pilot-details" :style="{ width: screenWidth,height: screenHeight}"> |
| | | <div id="landscapeBox" class="landscape-box" :style="{ width: screenWidth + 'px', height: (screenHeight) + 'px'}"> |
| | | <van-nav-bar |
| | | title="详情" |
| | | left-arrow |
| | | @click-left="onClickLogin" |
| | | /> |
| | | <div id="landscapeBox" class="landscape-box" :style="{ width: boxWidth + 'px', height: boxHeight + 'px'}"> |
| | | <div id="videoModule" class="l-video" :style="{ width: screenWidthVideo + 'px', height: (screenHeightVideo) + 'px'}"> |
| | | <video v-show="videoUrl" ref="videoPlayerBig" :style="{ width: screenWidthVideo + 'px', height: screenHeightVideo + 'px'}" controls autoplay muted playsinline style="text-align: left; object-fit: fill"> |
| | | Your browser is too old which doesn't support HTML5 video. |
| | | </video> |
| | | <el-empty v-show="videoUrl == ''" description="当前设备已关机,无法进行直播" :image="imageUrl"></el-empty> |
| | | <div v-if="videoUrl" class="center-point"></div> |
| | | <div class="right-box"> |
| | | <ptzControl :sn="sn" :osdVisible="sbInfo"/> |
| | | <controlConsole :sn="sn" :osdVisible="sbInfo" :cesiumViewe="globalViewer" /> |
| | | </div> |
| | | </div> |
| | | <div class="l-map" :style="{ width: screenWidthVideo + 'px', height: (screenHeightVideo) + 'px'}"> |
| | | <div id="cesiumContainerBigMap"></div> |
| | |
| | | <img v-else src="@/assets/images/suo.png" @touchstart="changeModelMap" /> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <div class="right-box"> |
| | | <ptzControl :sn="sn" :osdVisible="sbInfo" /> |
| | | <controlConsole :sn="sn" :osdVisible="sbInfo" :cesiumViewe="globalViewer" /> |
| | | </div> |
| | | </div> |
| | | </div> |
| | |
| | | // 使用 ref 来存储屏幕宽度和高度 |
| | | const screenWidth = ref(window.innerWidth); |
| | | const screenHeight = ref(window.innerHeight); |
| | | // 加了头部 |
| | | const boxWidth = ref(window.innerWidth); |
| | | const boxHeight = ref(window.innerHeight - 46); |
| | | // 地图切换 |
| | | const screenWidthMap = ref((window.innerHeight/2)*2)// ref(window.innerWidth); |
| | | const screenHeightMap = ref((window.innerHeight/2)); |
| | | const screenWidthMap = ref(boxWidth.value)// ref(((boxHeight.value)/2)*2) |
| | | const screenHeightMap = ref(((boxHeight.value)/2)); |
| | | // 视频切换 |
| | | const screenWidthVideo = ref(window.innerWidth); |
| | | const screenHeightVideo = ref(window.innerHeight/2); |
| | | const screenWidthVideo = ref(boxWidth.value); |
| | | const screenHeightVideo = ref(boxHeight.value/2); |
| | | const isBigMap = ref(false) |
| | | |
| | | // 显示高德地图矢量还是影像(2D/3D) |
| | |
| | | const drawCanvasHeight = ref<Number>(0) |
| | | |
| | | // 鹰眼视频 |
| | | const timer = null; |
| | | const videoPlayer = ref<any>(null); // 视频播放器实例 |
| | | let webrtcPlayer = null; // video.js 播放器 |
| | | // const timer = null; |
| | | |
| | | // 大屏幕视频 |
| | | const timerBig = null; |
| | | // const timerBig = null; |
| | | const videoPlayerBig = ref<any>(null); // 视频播放器实例 |
| | | let webrtcPlayerBig = null |
| | | |
| | |
| | | // 获取实时航线 |
| | | const getWrjSsLx = () => { |
| | | if (window.cesiumViewer== null) return |
| | | globalViewer = window.cesiumViewer; |
| | | getWaylineFile(sbInfo.sn).then((res:any) => { |
| | | if (res.code != 200) return |
| | | ssLinePath.value = res.data |
| | |
| | | case EBizCode.DeviceOsd: { |
| | | store.commit('SET_DEVICE_INFO', payload.data) |
| | | store.commit('SET_WS_MESSAGE', payload) |
| | | getviewDrone(payload) |
| | | break |
| | | } |
| | | case EBizCode.DockOsd: { // 机场 |
| | |
| | | } |
| | | case EBizCode.FlightTaskProgress: { // 获取进度 |
| | | // 如果点击列表进入没有获取到航线文件,那么这里重新请求一次 |
| | | console.log('航线有么111?',ssLinePath.value) |
| | | if (!ssLinePath || !ssLinePath.value) { |
| | | getWrjSsLx() |
| | | } |
| | | // if (!ssLinePath || !ssLinePath.value) { |
| | | // getWrjSsLx() |
| | | // } |
| | | break |
| | | } |
| | | } |
| | |
| | | // 监听ws 消息 |
| | | useConnectWebSocket(messageHandler, webSorketUrl) |
| | | |
| | | // 定义方法直接监听机场消息 |
| | | const getviewDrone = (newValue) => { |
| | | if (newValue.data.host) { |
| | | viewDroneInfo.longitude = newValue.data.host?.longitude |
| | | viewDroneInfo.latitude = newValue.data.host?.latitude |
| | | viewDroneInfo.elevation = Number(newValue.data.host?.elevation.toFixed(2)) + droneHeight.value |
| | | } |
| | | } |
| | | |
| | | let savedMapState = null; // 保存地图状态 |
| | | // 销毁之前先保存状态 |
| | | const destroyViewer = () => { |
| | | if ( window.cesiumViewer) { |
| | | // 保存当前地图状态(如相机位置) |
| | | // savedMapState = window.cesiumViewer.camera.view; |
| | | window.cesiumViewer.destroy(); |
| | | window.cesiumViewer = null; |
| | | } |
| | | } |
| | | |
| | | // let savedMapState = null; // 保存地图状态 |
| | | // // 销毁之前先保存状态 |
| | | // const destroyViewer = () => { |
| | | // if ( window.cesiumViewer) { |
| | | // // 保存当前地图状态(如相机位置) |
| | | // // savedMapState = window.cesiumViewer.camera.view; |
| | | // window.cesiumViewer.destroy(); |
| | | // window.cesiumViewer = null; |
| | | // } |
| | | // } |
| | | |
| | | // 视频全屏切换 |
| | | |
| | | // const changeModelVideo = (value:Boolean) => { |
| | | // const videoModule = document.getElementById('videoModule'); |
| | | // // 不管是否竖/横屏都一样 |
| | | // if (value) { // 全屏 |
| | | // videoModule.style.width = `${screenWidth.value}px` |
| | | // videoModule.style.height = `${screenHeight.value}px` |
| | | // } else { |
| | | // if (isLandscape.value) { |
| | | // videoModule.style.width = `${screenWidth.value/2}px` |
| | | // videoModule.style.height = `${screenHeight.value}px` |
| | | // } else { |
| | | // videoModule.style.height = `${screenHeight.value/2}px` |
| | | // videoModule.style.width = `${screenWidth.value}px` |
| | | // } |
| | | // } |
| | | // } |
| | | |
| | | // 地图全屏切换 |
| | | const changeModelMap = () => { |
| | |
| | | if (isLandscape.value) { // 横屏 |
| | | if (isBigMap.value) { // 全屏 |
| | | cesiumContainer.style.position = 'fixed'; |
| | | cesiumContainer.style.top = '0'; |
| | | cesiumContainer.style.top = '46px'; |
| | | cesiumContainer.style.left = '0'; |
| | | cesiumContainer.style.width = `${screenWidthMap.value * 2}px` |
| | | cesiumContainer.style.height = `${screenHeightMap.value}px` |
| | | cesiumContainer.style.width = `${boxWidth.value}px` |
| | | // cesiumContainer.style.height = `${screenHeightMap.value}px` |
| | | lZp.style.right = '40%'; |
| | | // lZoom.style.left = '0'; |
| | | } else { // 缩放 |
| | | cesiumContainer.style.width = `${screenWidthMap.value}px` |
| | | cesiumContainer.style.height = `${screenHeightMap.value}px` |
| | | cesiumContainer.style.width = `${boxWidth.value/2}px` |
| | | // cesiumContainer.style.height = `${screenHeightMap.value}px` |
| | | cesiumContainer.style.position = 'static'; |
| | | lZp.style.right = '16%'; |
| | | // lZoom.style.left = '50%'; |
| | | |
| | | } |
| | | } else { |
| | | if (isBigMap.value) { // 全屏 |
| | | if (isBigMap.value) { // 竖-全屏 |
| | | cesiumContainer.style.position = 'fixed'; |
| | | cesiumContainer.style.top = '0'; |
| | | cesiumContainer.style.top = '46px'; |
| | | cesiumContainer.style.left = '0'; |
| | | cesiumContainer.style.width = `${screenWidthMap.value}px` |
| | | cesiumContainer.style.height = `${screenHeightMap.value * 2}px` |
| | | // cesiumContainer.style.width = `${boxHeight.value}px` |
| | | cesiumContainer.style.height = `${boxHeight.value}px` |
| | | lZp.style.right = '30%'; |
| | | // lZoom.style.right = '0'; |
| | | } else { // 缩放 |
| | | console.log('竖屏宽度',screenWidthMap.value) |
| | | console.log('竖屏高度度',screenHeightMap.value) |
| | | cesiumContainer.style.width = `${screenWidthMap.value}px` |
| | | cesiumContainer.style.height = `${screenHeightMap.value}px` |
| | | cesiumContainer.style.width = `${boxHeight.value}px` |
| | | cesiumContainer.style.height = `${boxHeight.value/2}px` |
| | | cesiumContainer.style.position = 'static'; |
| | | lZp.style.right = '30%'; |
| | | // lZoom.style.right = '0'; |
| | |
| | | // }; |
| | | |
| | | const checkOrientation = async () => { |
| | | screenWidth.value = window.innerWidth; |
| | | screenHeight.value = window.innerHeight; |
| | | console.log('屏幕宽度', screenWidth.value) |
| | | console.log('屏幕高度', screenHeight.value) |
| | | boxWidth.value = window.innerWidth; |
| | | boxHeight.value = window.innerHeight - 46; |
| | | const landscapeBox = document.getElementById('landscapeBox'); |
| | | let cesiumContainer = document.getElementById('cesiumContainerBigMap'); |
| | | const videoModule = document.getElementById('videoModule'); |
| | | const lZp = document.getElementById('lZp'); |
| | | const lZoom = document.getElementById('lZoom'); |
| | | |
| | | if (window.innerWidth > window.innerHeight) {//横屏 |
| | | screenWidthVideo.value = window.innerWidth/2 |
| | | screenHeightVideo.value = window.innerHeight |
| | | screenWidthVideo.value = boxWidth.value/2 |
| | | screenHeightVideo.value = boxHeight.value |
| | | isLandscape.value = true; |
| | | // updateScreenSize() |
| | | landscapeBox.style.display = 'flex'; |
| | | lZp.style.right = '16%'; |
| | | lZoom.style.position = 'fixed'; |
| | | cesiumContainer.style.width = `${screenWidth.value/2}px` |
| | | cesiumContainer.style.height = `${screenHeight.value}px` |
| | | cesiumContainer.style.width = `${boxWidth.value/2}px` |
| | | cesiumContainer.style.height = `${boxHeight.value}px` |
| | | cesiumContainer.style.position = 'static' |
| | | cesiumContainer.style.right = '0' |
| | | cesiumContainer.style.marginLeft = '0%' |
| | | } else { //竖屏 |
| | | isLandscape.value = false; |
| | | screenWidthVideo.value = window.innerWidth |
| | | screenHeightVideo.value = (window.innerHeight/2) |
| | | screenWidthVideo.value = boxWidth.value |
| | | screenHeightVideo.value = boxHeight.value/2 |
| | | // updateScreenSize() |
| | | landscapeBox.style.display = 'inherit'; |
| | | landscapeBox.style.overflow = 'hidden'; |
| | | lZp.style.right = '32%'; |
| | | cesiumContainer.style.width = `${screenHeight.value}px` |
| | | cesiumContainer.style.height = `${screenHeight.value/2}px` |
| | | cesiumContainer.style.width = `${boxHeight.value}px` |
| | | cesiumContainer.style.height = `${boxHeight.value/2}px` |
| | | // cesiumContainer.style.position = 'absolute' |
| | | cesiumContainer.style.marginLeft = '-32%' |
| | | } |
| | |
| | | } |
| | | }, {deep: true}) |
| | | |
| | | watch(() => store.state.wsMessage, (newValue, oldValue) => { |
| | | // 控制台报 Expected longitude to be typeof number, actual typeof was undefined 是因为这里没有及时拿到longitude数据 |
| | | if (newValue) { |
| | | viewDroneInfo.longitude = newValue?.longitude |
| | | viewDroneInfo.latitude = newValue?.latitude |
| | | viewDroneInfo.elevation = Number(newValue?.elevation.toFixed(2)) + droneHeight.value |
| | | } |
| | | }, {deep: true}) |
| | | // watch(() => store.state.wsMessage, (newValue, oldValue) => { |
| | | // // 控制台报 Expected longitude to be typeof number, actual typeof was undefined 是因为这里没有及时拿到longitude数据 |
| | | // if (newValue) { |
| | | // viewDroneInfo.longitude = newValue?.longitude |
| | | // viewDroneInfo.latitude = newValue?.latitude |
| | | // viewDroneInfo.elevation = Number(newValue?.elevation.toFixed(2)) + droneHeight.value |
| | | // } |
| | | // }, {deep: true}) |
| | | |
| | | |
| | | let intervalId = null; |
| | | |
| | | const onClickLogin = () => { |
| | | history.back(); |
| | | } |
| | | |
| | | // 为组件赋值 |
| | | const getGlobalViewer = () => { |
| | | globalViewer = window.cesiumViewer; |
| | | } |
| | | |
| | | onMounted(async () => { |
| | | window.addEventListener('orientationchange', checkOrientation); |
| | |
| | | loadVideo() |
| | | await nextTick(); |
| | | await _init('cesiumContainerBigMap'); |
| | | await getGlobalViewer() |
| | | await getWrjSsLx(); |
| | | // 判断是遥控器还是机场 |
| | | let domain = sessionStorage.getItem('domain') |
| | | if (domain !== '0') { |
| | | await getPhoneLocation(workspaceId.value) |
| | | clickPhoneLocation() |
| | | // // // 设置定时器,每隔 5 秒刷新一次数据 |
| | | intervalId = setInterval(()=> { |
| | | getPhoneLocation(workspaceId.value) |
| | | }, 5000); |
| | | // intervalId = setInterval(()=> { |
| | | // getPhoneLocation(workspaceId.value) |
| | | // }, 5000); |
| | | } |
| | | }); |
| | | |
| | |
| | | transform: translate(-50%, -50%); |
| | | justify-content: center; |
| | | } |
| | | .right-box {} |
| | | } |
| | | #cesiumContainerBigMap { |
| | | width: 100%; |
| | |
| | | } |
| | | } |
| | | } |
| | | .right-box {} |
| | | |
| | | } |
| | | </style> |
| | |
| | | <template> |
| | | <div class="drone-pilot-list-warp"> |
| | | <van-nav-bar |
| | | title="机场/遥控器" |
| | | left-arrow |
| | | @click-left="onClickLogin" |
| | | /> |
| | | <div class="drone-pilot-list"> |
| | | <template v-if="droneOrPilotTxt == 'drone'"> |
| | | <div class="box-info" v-for="item in cardList" :key="item.sn"> |
| | |
| | | |
| | | } |
| | | |
| | | const onClickLogin = () => { |
| | | history.back(); |
| | | } |
| | | |
| | | onMounted(() => { |
| | | init() |
| | | // 设置定时器,每隔 3 秒刷新一次数据 |
| | |
| | | background-color: #b8b3b338; |
| | | // background-image: url('@/assets/images/newcon_box.png'); |
| | | .drone-pilot-list { |
| | | height: calc(100vh - 10%); |
| | | height: calc(100vh - 50px - 46px); |
| | | overflow: auto; |
| | | .box-info { |
| | | background-color: #fff; |
| | |
| | | .control-console { |
| | | |
| | | .control { |
| | | position: fixed; |
| | | top: 0.2rem; |
| | | left: 0.2rem; |
| | | position: absolute; |
| | | bottom: 12.8rem; |
| | | right: 0.2rem; |
| | | width: 2rem; |
| | | height: 2rem; |
| | | border-radius: 3px; |
| New file |
| | |
| | | <template> |
| | | <div class="ptz-control"> |
| | | <!-- <div class="ptz" :class="isPtz?'actived-blue':''"> |
| | | <img src="@/assets/images/yt.png" @touchstart="ptzClick" /> |
| | | </div> --> |
| | | <div class="btn-group"> |
| | | <div class="btn-item" :class="isPtz?'actived-blue':''" @touchstart="ptzClick"> |
| | | <img src="@/assets/images/yt.png" /> |
| | | </div> |
| | | <div class="btn-item" @touchstart="onTakeCameraPhoto"> |
| | | <img src="@/assets/images/photo.png" /> |
| | | </div> |
| | | <div class="btn-item" v-if="!isCameraRecording" @touchstart="onStartCameraRecording"> |
| | | <img src="@/assets/images/sx-open.png" /> |
| | | </div> |
| | | <div class="btn-item" v-else @touchstart="onStopCameraRecording"> |
| | | <img src="@/assets/images/sx-close.png"/> |
| | | </div> |
| | | <div class="btn-item" @touchstart="onZoom(true)"> |
| | | <img src="@/assets/images/fangda.png" /> |
| | | </div> |
| | | <div class="btn-item" @touchstart="onZoom(false)"> |
| | | <img src="@/assets/images/suoxiao.png" /> |
| | | </div> |
| | | <!-- <div class="btn-item"> |
| | | <img v-if="!isBigVideo" src="@/assets/images/fang.png" @touchstart="changeModelVideo(true)" /> |
| | | <img v-else src="@/assets/images/suo.png" @touchstart="changeModelVideo(false)" /> |
| | | </div> --> |
| | | </div> |
| | | <div class="circle-box" v-show="isPtz"> |
| | | <div class="top" @click="ytClick('up')" @mousedown="onMouseDown(KeyCode.ARROW_UP)" @mouseup="onMouseUp" |
| | | @touchstart="onMouseDown(KeyCode.ARROW_UP)" @touchend="onMouseUp" @mouseleave="onMouseUp"></div> |
| | | <div class="left" @click="ytClick('left')" @mousedown="onMouseDown(KeyCode.ARROW_LEFT)" @mouseup="onMouseUp" |
| | | @touchstart="onMouseDown(KeyCode.ARROW_LEFT)" @touchend="onMouseUp" @mouseleave="onMouseUp"></div> |
| | | <div class="bottom" @click="ytClick('down')" @mousedown="onMouseDown(KeyCode.ARROW_DOWN)" @mouseup="onMouseUp" |
| | | @touchstart="onMouseDown(KeyCode.ARROW_DOWN)" @touchend="onMouseUp" @mouseleave="onMouseUp"></div> |
| | | <div class="right" @click="ytClick('right')" @mousedown="onMouseDown(KeyCode.ARROW_RIGHT)" @mouseup="onMouseUp" |
| | | @touchstart="onMouseDown(KeyCode.ARROW_RIGHT)" @touchend="onMouseUp" @mouseleave="onMouseUp"></div> |
| | | <div class="reset-center" @click="onResetGimbal(0)"> |
| | | <el-icon><Refresh /></el-icon> |
| | | </div> |
| | | <div class="blue-bgc"></div> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script lang="ts" setup> |
| | | import { ElMessage } from 'element-plus' |
| | | import { Refresh } from '@element-plus/icons-vue'; |
| | | import { ControlSource, EModeCode } from '@/types/device' |
| | | import { CameraMode } from '@/types/live-stream' |
| | | import { ref, onMounted, onBeforeUnmount, nextTick, reactive, computed, watch } from 'vue'; |
| | | import { postPayloadCommands, postPayloadAuth } from '@/api/drone/payload' |
| | | import { switchLivestream } from '@/api/drone' |
| | | import { usePayloadControl } from './use-payload-control' |
| | | import { useDroneControlWsEvent } from '@/utils/websocket/drone-ws-control' |
| | | import cesiumOperation from '@/utils/cesium-tsa.js'; |
| | | import { useMyStore } from '@/store' |
| | | import { fa } from 'element-plus/es/locale'; |
| | | |
| | | const { loadLAYER } = cesiumOperation() |
| | | |
| | | const { |
| | | checkPayloadAuth, |
| | | authPayload, |
| | | resetGimbal, |
| | | switchCameraMode, |
| | | takeCameraPhoto, |
| | | startCameraRecording, |
| | | stopCameraRecording, |
| | | changeCameraFocalLength, |
| | | } = usePayloadControl() |
| | | |
| | | const props = defineProps({ |
| | | sn: { |
| | | type: String, |
| | | required: true, |
| | | }, |
| | | osdVisible: { |
| | | type: Object, |
| | | required: true, |
| | | }, |
| | | cesiumViewe: { |
| | | type: Object, |
| | | required: true, |
| | | } |
| | | }); |
| | | const emitEvents = defineEmits(['changeModelVideo']) |
| | | |
| | | // 放大和缩小 |
| | | // let isBigVideo = ref<Boolean>(false) |
| | | |
| | | // 录像 |
| | | let isCameraRecording = ref<Boolean>(false) |
| | | // |
| | | let isActionComplete = ref<Boolean>(false) |
| | | |
| | | let payloadCameraMode = ref<String>('wide') |
| | | |
| | | // 缩放监听更新值 |
| | | let zoom_factor_watch = ref(0) |
| | | // 缩放初始值 |
| | | let zoom_factor_start = ref(0) |
| | | // 缩放变化值 |
| | | let zoom_factor = ref(0) |
| | | // is_First 记录第一次 |
| | | let is_First = ref<Boolean>(false) |
| | | |
| | | // 是否显示云台图标 |
| | | let isPtz = ref(false) |
| | | |
| | | // 显示高德地图矢量还是影像(2D/3D) |
| | | // let is2d = ref(true) |
| | | |
| | | let yaw_speed = ref((payloadCameraMode.value == 'wide'? 1 : Math.ceil(zoom_factor.value))*10) // 云台横向角度 |
| | | let pitch_speed = ref((payloadCameraMode.value == 'wide'? 1 : Math.ceil(zoom_factor.value))*10) // 云台纵向角度 |
| | | |
| | | let resultProps = reactive<any>(props.osdVisible.children.payloads_list[0]) |
| | | // console.log(resultProps, '现在勒') |
| | | const store = useMyStore() |
| | | // 负载控制 |
| | | const payloadSelectInfo = reactive<any>({ |
| | | value: resultProps.payload_sn, |
| | | controlSource: resultProps.control_source, |
| | | options: [], |
| | | payloadIndex: resultProps.payload_index, |
| | | camera: '', // 当前负载osd信息 |
| | | }) |
| | | |
| | | watch(() => store.state.wsMessage, (newValue, oldValue) => { |
| | | if (newValue && newValue.cameras) { |
| | | // console.log('监控值', newValue.cameras) |
| | | zoom_factor_watch.value = Math.ceil(newValue.cameras[0].zoom_factor) |
| | | if (!is_First.value) { |
| | | zoom_factor.value = zoom_factor_watch.value |
| | | zoom_factor_start.value = zoom_factor_watch.value |
| | | is_First.value = true |
| | | } |
| | | |
| | | payloadSelectInfo.camera = newValue.cameras.find( |
| | | (item:any) => |
| | | item.payload_index === payloadSelectInfo.payloadIndex, |
| | | ) |
| | | } else { |
| | | payloadSelectInfo.camera = undefined |
| | | } |
| | | }, |
| | | { |
| | | deep: true, |
| | | }) |
| | | // ws 消息通知 |
| | | const { droneControlSource, payloadControlSource } = useDroneControlWsEvent(props.sn, payloadSelectInfo.value) |
| | | watch(() => payloadControlSource, (controlSource) => { |
| | | payloadSelectInfo.controlSource = 'A'// controlSource.value |
| | | }, { |
| | | immediate: true, |
| | | deep: true |
| | | }) |
| | | |
| | | |
| | | // 切换是否出现云台上下左右图 |
| | | const ptzClick = () => { |
| | | isPtz.value = !isPtz.value |
| | | } |
| | | |
| | | // 拍照 |
| | | const onTakeCameraPhoto = () => { |
| | | if (!checkPayloadAuth(payloadSelectInfo.controlSource)) { |
| | | return |
| | | } |
| | | |
| | | if (isActionComplete.value) { |
| | | ElMessage.warning('当前相机动作暂未完成,请勿点击!!') |
| | | return |
| | | } |
| | | if (payloadSelectInfo.camera?.recording_state) { |
| | | ElMessage.warning('当前摄像头正在录制视频,请先结束视频录制!!') |
| | | return |
| | | } |
| | | |
| | | if (payloadSelectInfo.camera?.camera_mode !== CameraMode.Photo) { |
| | | const res = switchCamera(CameraMode.Photo) |
| | | res.then((response:any) => { |
| | | if (!response) return |
| | | |
| | | if (payloadSelectInfo.camera?.camera_mode !== CameraMode.Photo) { |
| | | curTaskCameraPhoto(2000) |
| | | } else { |
| | | curTaskCameraPhoto(0) |
| | | } |
| | | }) |
| | | |
| | | return |
| | | } |
| | | |
| | | curTaskCameraPhoto(0) |
| | | } |
| | | const curTaskCameraPhoto = (time:any) => { |
| | | isActionComplete.value = true |
| | | |
| | | ElMessage({ |
| | | message: '请稍后,正在拍摄中。。。', |
| | | duration: 2000, |
| | | }) |
| | | setTimeout(() => { |
| | | let cameraType = ref('wide') |
| | | if (payloadCameraMode.value === 'zoom') { |
| | | cameraType.value = payloadCameraMode |
| | | } |
| | | console.log('1111',cameraType.value) |
| | | takeCameraPhoto( |
| | | props.osdVisible.dockSn, |
| | | payloadSelectInfo.payloadIndex, |
| | | cameraType.value |
| | | ).then((photoRes:any) => { |
| | | if (!photoRes) { |
| | | return |
| | | } |
| | | |
| | | isActionComplete.value = false |
| | | }) |
| | | }, time) |
| | | } |
| | | const switchCamera = (camera_mode:any) => { |
| | | if (!checkPayloadAuth(payloadSelectInfo.controlSource)) { |
| | | return |
| | | } |
| | | return switchCameraMode(props.osdVisible.dockSn, { |
| | | payload_index: payloadSelectInfo.payloadIndex, |
| | | camera_mode: camera_mode, |
| | | video_type: payloadCameraMode.value, |
| | | }) |
| | | } |
| | | |
| | | // 相机操作 |
| | | const onAuthPayload = () => { |
| | | //负载控制 |
| | | authPayload(props.osdVisible.dockSn, payloadSelectInfo.payloadIndex).then( |
| | | (payloadRes:any) => { |
| | | if (payloadRes.code !== 0) |
| | | return ElMessage.error('获得有效载荷控制失败,请稍后重试!!') |
| | | payloadControlSource.value = ControlSource.A |
| | | ElMessage.success('成功获得有效载荷控制') |
| | | }, |
| | | ) |
| | | } |
| | | // 开始录像 |
| | | const onStartCameraRecording = () => { |
| | | if (!checkPayloadAuth(payloadSelectInfo.controlSource)) { |
| | | return |
| | | } |
| | | if (isActionComplete.value) { |
| | | ElMessage.warning('当前相机动作暂未完成,请勿点击!!') |
| | | return |
| | | } |
| | | |
| | | if (payloadSelectInfo.camera?.recording_state) { |
| | | return ElMessage.warning( |
| | | '当前摄像头正在录制视频,请勿重复点击!!', |
| | | ) |
| | | } |
| | | |
| | | if (payloadSelectInfo.camera?.camera_mode !== CameraMode.Video) { |
| | | const res = switchCamera(CameraMode.Video) |
| | | res.then((response) => { |
| | | if (!response) return |
| | | if (payloadSelectInfo.camera?.camera_mode !== CameraMode.Video) { |
| | | curStartCameraRecording(2000) |
| | | } else { |
| | | curStartCameraRecording(0) |
| | | } |
| | | }) |
| | | |
| | | return |
| | | } |
| | | |
| | | curStartCameraRecording(0) |
| | | } |
| | | |
| | | const curStartCameraRecording = (time:any) => { |
| | | isActionComplete.value = true |
| | | |
| | | ElMessage({ |
| | | message: '请稍后,正在开启视频录制。。。', |
| | | duration: 2000, |
| | | }) |
| | | |
| | | setTimeout(() => { |
| | | startCameraRecording(props.osdVisible.dockSn, payloadSelectInfo.payloadIndex) |
| | | .then((res) => { |
| | | if (res.code !== 0) |
| | | return ElMessage.warning('开始录制失败,请稍后重试!!') |
| | | ElMessage.success('开始录制成功') |
| | | // 开始录像 |
| | | isCameraRecording.value = true |
| | | isActionComplete.value = false |
| | | }).catch((e) => { |
| | | isActionComplete.value = false |
| | | }) |
| | | }, time) |
| | | } |
| | | |
| | | // 停止录像 |
| | | const onStopCameraRecording = () => { |
| | | if (!checkPayloadAuth(payloadSelectInfo.controlSource)) { |
| | | return |
| | | } |
| | | if (isActionComplete.value) { |
| | | ElMessage.warning('当前相机动作暂未完成,请勿点击!!') |
| | | return |
| | | } |
| | | isActionComplete.value = true |
| | | stopCameraRecording(props.osdVisible.dockSn, payloadSelectInfo.payloadIndex) |
| | | .then((res) => { |
| | | if (res.code !== 0) |
| | | return ElMessage.warning('停止录制失败,请稍后重试!!') |
| | | ElMessage.success('停止录制成功') |
| | | // 停止录像 |
| | | isCameraRecording.value = false |
| | | isActionComplete.value = false |
| | | }) |
| | | .catch((e) => { |
| | | isActionComplete.value = false |
| | | }) |
| | | } |
| | | |
| | | |
| | | // 缩放 |
| | | const onZoom = (value:any) => { |
| | | if (value) { |
| | | zoom_factor.value += 1 |
| | | if (zoom_factor.value >= 200) { |
| | | zoom_factor.value = 200 |
| | | } |
| | | } else { |
| | | zoom_factor.value -= 1 |
| | | if (zoom_factor.value <= 2) { |
| | | zoom_factor.value = 2 |
| | | } |
| | | console.log('9999', zoom_factor.value) |
| | | } |
| | | switchLivestream({ |
| | | video_id: `${props.osdVisible.dockSn}/${payloadSelectInfo.payloadIndex}/normal-0`, |
| | | video_type: 'zoom', |
| | | }).then((res:any) => { |
| | | if (res.data.code != 0) return |
| | | // 只考虑 广角:wide模式 |
| | | changeCameraFocalLength(props.osdVisible.dockSn, { |
| | | payload_index: payloadSelectInfo.payloadIndex, |
| | | camera_type: 'zoom', |
| | | zoom_factor: zoom_factor.value, |
| | | }, value) |
| | | }) |
| | | } |
| | | |
| | | // const onChangeD = () => { |
| | | // is2d.value = !is2d.value |
| | | // store.commit('SET_MAP_SETTING_MODE', is2d.value?2:3); |
| | | // loadLAYER(); |
| | | // } |
| | | |
| | | // 重置云台角度 |
| | | const onResetGimbal = async (resetMode:any) => { |
| | | try { |
| | | await resetGimbal(props.osdVisible.dockSn, { |
| | | payload_index: payloadSelectInfo.payloadIndex, |
| | | reset_mode: resetMode, |
| | | }) |
| | | await changeCameraFocalLength(props.osdVisible.dockSn, { |
| | | payload_index: payloadSelectInfo.payloadIndex, |
| | | camera_type: 'zoom', |
| | | zoom_factor: zoom_factor_start.value, |
| | | }) |
| | | // this.zoomMultiple = 8 // 设置步长 |
| | | } catch (err) { |
| | | console.log(err) |
| | | } |
| | | } |
| | | // 记得加loading |
| | | // 云台上 下 左 右 |
| | | const ytClick = async (value:String) => { |
| | | if (payloadCameraMode.value == 'zoom' && zoom_factor.value > 35 && (value == 'up' || value == 'right')) { pitch_speed.value = 350 } |
| | | if (payloadCameraMode.value == 'zoom' && zoom_factor.value > 35 && (value == 'left' || value == 'down')) { pitch_speed.value = -350 } |
| | | if (value == 'up' || value == 'down') { |
| | | postPayloadCommands(props.osdVisible.dockSn, {cmd: 'camera_screen_drag', |
| | | data: { |
| | | locked: false, |
| | | payload_index: payloadSelectInfo.payloadIndex, |
| | | pitch_speed: value == 'up' ? pitch_speed.value : -pitch_speed.value, |
| | | yaw_speed: 0, |
| | | }}) |
| | | } else if ((value == 'left' || value == 'right')) { |
| | | postPayloadCommands(props.osdVisible.dockSn, {cmd: 'camera_screen_drag', |
| | | data: { |
| | | locked: false, |
| | | payload_index: payloadSelectInfo.payloadIndex, |
| | | pitch_speed: 0, |
| | | yaw_speed: value == 'right' ? pitch_speed.value : -pitch_speed.value, |
| | | }}) |
| | | } |
| | | } |
| | | |
| | | // 视频大小的缩放 |
| | | // const changeModelVideo = (value:Boolean) => { |
| | | // isBigVideo.value = value |
| | | // emitEvents('changeModelVideo', value) |
| | | // } |
| | | |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .ptz-control { |
| | | .btn-group { |
| | | position: absolute; |
| | | left: 0.2rem; |
| | | bottom: 2rem; |
| | | .btn-item { |
| | | width: 2rem; |
| | | height: 2rem; |
| | | border-radius: 3px; |
| | | background-color: rgba(0, 0, 0, 0.5); |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | overflow: hidden; |
| | | cursor: pointer; |
| | | margin-bottom: 0.5rem; |
| | | pointer-events: all; |
| | | img { |
| | | width: 1.6rem; |
| | | height: 1.6rem; |
| | | } |
| | | } |
| | | .actived-blue { |
| | | background-image: none; |
| | | background-color: rgba(23, 124, 198, 0.7); |
| | | } |
| | | } |
| | | .circle-box { |
| | | width: 100px; |
| | | height: 100px; |
| | | border-radius: 50%; |
| | | overflow: hidden; |
| | | background-color: rgba(0, 0, 0, 0.5); |
| | | position: fixed; |
| | | top: 0; |
| | | right: 4rem; |
| | | |
| | | .top, |
| | | .left, |
| | | .bottom, |
| | | .right { |
| | | width: 10px; |
| | | height: 10px; |
| | | position: absolute; |
| | | cursor: pointer; |
| | | z-index: 2; |
| | | } |
| | | |
| | | .top { |
| | | top: -2px; |
| | | left: 50%; |
| | | transform: translateX(-50%); |
| | | border-top: 10px solid transparent; |
| | | border-right: 10px solid transparent; |
| | | border-left: 10px solid transparent; |
| | | border-bottom: 10px solid #fff; |
| | | |
| | | &:hover { |
| | | &~.blue-bgc { |
| | | border-top: 50px solid #409eff; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .left { |
| | | top: 50%; |
| | | left: -2px; |
| | | transform: translateY(-50%); |
| | | border-top: 10px solid transparent; |
| | | border-right: 10px solid #fff; |
| | | border-left: 10px solid transparent; |
| | | border-bottom: 10px solid transparent; |
| | | |
| | | &:hover { |
| | | &~.blue-bgc { |
| | | border-left: 50px solid #409eff; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .bottom { |
| | | bottom: -2px; |
| | | left: 50%; |
| | | transform: translateX(-50%); |
| | | border-top: 10px solid #fff; |
| | | border-right: 10px solid transparent; |
| | | border-left: 10px solid transparent; |
| | | border-bottom: 10px solid transparent; |
| | | |
| | | &:hover { |
| | | &~.blue-bgc { |
| | | border-bottom: 50px solid #409eff; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .right { |
| | | top: 50%; |
| | | right: -2px; |
| | | transform: translateY(-50%); |
| | | border-top: 10px solid transparent; |
| | | border-right: 10px solid transparent; |
| | | border-left: 10px solid #fff; |
| | | border-bottom: 10px solid transparent; |
| | | |
| | | &:hover { |
| | | &~.blue-bgc { |
| | | border-right: 50px solid #409eff; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .reset-center { |
| | | position: absolute; |
| | | width: 40%; |
| | | height: 40%; |
| | | left: 50%; |
| | | top: 50%; |
| | | transform: translate(-50%, -50%); |
| | | border-radius: 50%; |
| | | background-color: rgb(63, 66, 68); |
| | | z-index: 2; |
| | | cursor: pointer; |
| | | display: flex; |
| | | justify-content: center; |
| | | align-items: center; |
| | | |
| | | i { |
| | | color: #fff; |
| | | font-size: 20px; |
| | | font-weight: bolder; |
| | | } |
| | | |
| | | &:hover { |
| | | background-color: #409eff; |
| | | } |
| | | } |
| | | |
| | | .blue-bgc { |
| | | width: 100px; |
| | | height: 100px; |
| | | position: absolute; |
| | | top: 0; |
| | | left: 0; |
| | | z-index: 1; |
| | | border-top: 50px solid transparent; |
| | | border-right: 50px solid transparent; |
| | | border-left: 50px solid transparent; |
| | | border-bottom: 50px solid transparent; |
| | | } |
| | | } |
| | | } |
| | | </style> |
| | |
| | | <template> |
| | | <div class="ptz-control"> |
| | | <div class="ptz" :class="isPtz?'actived-blue':''"> |
| | | <img src="@/assets/images/yt.png" @touchstart="ptzClick" /> |
| | | </div> |
| | | <div class="btn-group"> |
| | | <div class="btn-item" :class="isPtz?'actived-blue':''" @touchstart="ptzClick"> |
| | | <img src="@/assets/images/yt.png" /> |
| | | </div> |
| | | <div class="btn-item" @touchstart="onTakeCameraPhoto"> |
| | | <img src="@/assets/images/photo.png" /> |
| | | </div> |
| | |
| | | <div class="btn-item" @touchstart="onZoom(false)"> |
| | | <img src="@/assets/images/suoxiao.png" /> |
| | | </div> |
| | | <!-- <div class="btn-item" @touchstart="onChangeD"> |
| | | <img v-if="is2d" src="@/assets/images/stand.png" /> |
| | | <img v-else src="@/assets/images/satellite.png" /> |
| | | <!-- <div class="btn-item"> |
| | | <img v-if="!isBigVideo" src="@/assets/images/fang.png" @touchstart="changeModelVideo(true)" /> |
| | | <img v-else src="@/assets/images/suo.png" @touchstart="changeModelVideo(false)" /> |
| | | </div> --> |
| | | </div> |
| | | <div class="circle-box" v-show="isPtz"> |
| | | <div class="top" @click="ytClick('up')" @mousedown="onMouseDown(KeyCode.ARROW_UP)" @mouseup="onMouseUp" |
| | | @touchstart="onMouseDown(KeyCode.ARROW_UP)" @touchend="onMouseUp" @mouseleave="onMouseUp"></div> |
| | | <div class="left" @click="ytClick('left')" @mousedown="onMouseDown(KeyCode.ARROW_LEFT)" @mouseup="onMouseUp" |
| | | @touchstart="onMouseDown(KeyCode.ARROW_LEFT)" @touchend="onMouseUp" @mouseleave="onMouseUp"></div> |
| | | <div class="bottom" @click="ytClick('down')" @mousedown="onMouseDown(KeyCode.ARROW_DOWN)" @mouseup="onMouseUp" |
| | | @touchstart="onMouseDown(KeyCode.ARROW_DOWN)" @touchend="onMouseUp" @mouseleave="onMouseUp"></div> |
| | | <div class="right" @click="ytClick('right')" @mousedown="onMouseDown(KeyCode.ARROW_RIGHT)" @mouseup="onMouseUp" |
| | | @touchstart="onMouseDown(KeyCode.ARROW_RIGHT)" @touchend="onMouseUp" @mouseleave="onMouseUp"></div> |
| | | <div class="top" @click="ytClick('up')"></div> |
| | | <div class="left" @click="ytClick('left')"></div> |
| | | <div class="bottom" @click="ytClick('down')"></div> |
| | | <div class="right" @click="ytClick('right')"></div> |
| | | <div class="reset-center" @click="onResetGimbal(0)"> |
| | | <el-icon><Refresh /></el-icon> |
| | | </div> |
| | |
| | | import { useDroneControlWsEvent } from '@/utils/websocket/drone-ws-control' |
| | | import cesiumOperation from '@/utils/cesium-tsa.js'; |
| | | import { useMyStore } from '@/store' |
| | | import { fa } from 'element-plus/es/locale'; |
| | | |
| | | const { loadLAYER } = cesiumOperation() |
| | | |
| | |
| | | required: true, |
| | | } |
| | | }); |
| | | const emitEvents = defineEmits(['changeModelVideo']) |
| | | |
| | | // 放大和缩小 |
| | | // let isBigVideo = ref<Boolean>(false) |
| | | |
| | | // 录像 |
| | | let isCameraRecording = ref<Boolean>(false) |
| | |
| | | } |
| | | // 开始录像 |
| | | const onStartCameraRecording = () => { |
| | | console.log('设施',payloadSelectInfo.controlSource) |
| | | if (!checkPayloadAuth(payloadSelectInfo.controlSource)) { |
| | | return |
| | | } |
| | |
| | | } |
| | | } |
| | | |
| | | // 视频大小的缩放 |
| | | // const changeModelVideo = (value:Boolean) => { |
| | | // isBigVideo.value = value |
| | | // emitEvents('changeModelVideo', value) |
| | | // } |
| | | |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .ptz-control { |
| | | .ptz { |
| | | position: fixed; |
| | | top: 3rem; |
| | | left: 0.2rem; |
| | | width: 2rem; |
| | | height: 2rem; |
| | | border-radius: 3px; |
| | | background-color: rgba(0, 0, 0, 0.5); |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | overflow: hidden; |
| | | cursor: pointer; |
| | | margin-bottom: 1rem; |
| | | pointer-events: all; |
| | | img { |
| | | width: 1.6rem; |
| | | height: 1.6rem; |
| | | } |
| | | } |
| | | .actived-blue { |
| | | background-image: none; |
| | | background-color: rgba(23, 124, 198, 0.7); |
| | | } |
| | | .btn-group { |
| | | position: fixed; |
| | | top: 5.8rem; |
| | | left: 0.2rem; |
| | | // padding: 4px; |
| | | position: absolute; |
| | | right: 0.2rem; |
| | | bottom: 0rem; |
| | | .btn-item { |
| | | width: 2rem; |
| | | height: 2rem; |
| | |
| | | justify-content: center; |
| | | overflow: hidden; |
| | | cursor: pointer; |
| | | margin-bottom: 1rem; |
| | | margin-bottom: 0.5rem; |
| | | pointer-events: all; |
| | | img { |
| | | width: 1.6rem; |
| | | height: 1.6rem; |
| | | } |
| | | } |
| | | .actived-blue { |
| | | background-image: none; |
| | | background-color: rgba(23, 124, 198, 0.7); |
| | | } |
| | | } |
| | | .circle-box { |
| | |
| | | overflow: hidden; |
| | | background-color: rgba(0, 0, 0, 0.5); |
| | | position: fixed; |
| | | top: 0; |
| | | right: 4rem; |
| | | top: 20%; |
| | | left: 1%; |
| | | |
| | | .top, |
| | | .left, |
| New file |
| | |
| | | |
| | | import { EDeviceTypeName, EHmsLevel } from '@/types/enums' |
| | | import cesiumOperation from '@/utils/cesium-tsa' |
| | | import { analyzeKmzFile, XMLToJSON } from '@/utils/cesium/kmz.js' |
| | | import IndexedDBService from '@/utils/indexDB' |
| | | import CreateFrustum from '@/utils/cesium/frustum/CreateFrustum' |
| | | import { useMyStore } from '@/store' |
| | | import { getTwoPointInfo } from '@/api/manage' |
| | | |
| | | const { getEntityById, removeById, globalCesium, addLeftClickEvent, removeLeftClickEvent, flyTo } = cesiumOperation() |
| | | // 无人机实体 |
| | | let droneEntity = null |
| | | |
| | | // 激光实体 |
| | | let laserEntity = null |
| | | |
| | | // 两点之间线实体 |
| | | let twoLineEntity = null |
| | | let myAppEntity = null; |
| | | export function droneFly () { |
| | | const store = useMyStore() |
| | | let deviceInfo = {} // 无人机信息 |
| | | let dockInfo = {} // 机场信息 |
| | | let previousPositions = [] |
| | | let wId = '' |
| | | let snCode = [] |
| | | let viewDroneInfo = {} // 获取实时经纬度 |
| | | let waylinePoints = [] // 当前航线点 |
| | | let currentTaskCreateTime = null // 当前飞行任务创建时间 |
| | | let switchSaving = true // 有selectedWorkSpaceId 就为true |
| | | // 航线飞行处于第几个点位 |
| | | let currentWaypointIndex = 0 |
| | | // 速度集合 |
| | | let droneSpeedArr = [] |
| | | let lastTime = null |
| | | let viewInfoFrustum = null |
| | | |
| | | // 手机设备定位 |
| | | let me_latitude = 0; |
| | | let me_longitude = 0; |
| | | let me_altitude = 0; |
| | | // 扫射定位 |
| | | let measure_target_longitude = 0; |
| | | let measure_target_latitude = 0; |
| | | let measure_target_altitude = 0; |
| | | |
| | | |
| | | async function initDock(data:any, sn:any, dockSn:any, workspaceId:any, ssLinePath:any, viewDroneInfo:any) { |
| | | // 获取workspaceId |
| | | wId = workspaceId |
| | | // 给无人机参数赋值 |
| | | deviceInfo = data.deviceInfo[`${sn}`] |
| | | // 机场信息 |
| | | dockInfo = data.dockInfo[`${dockSn}`] |
| | | viewDroneInfo = viewDroneInfo |
| | | console.log('导致地图不显示原因1',viewDroneInfo.longitude) |
| | | // 当前飞行任务创建时间 |
| | | if (data.currentType === EDeviceTypeName.Aircraft && deviceInfo) { |
| | | const droneLngLat = { |
| | | latitude: deviceInfo.latitude || 0, |
| | | longitude: deviceInfo.longitude || 0, |
| | | } |
| | | // 当飞机模型存在 |
| | | if (droneEntity) { |
| | | // 且在空中飞行时,实时更新飞机模型的位置 |
| | | if (deviceInfo.mode_code && [3, 4, 5, 9, 10, 16].includes(deviceInfo.mode_code)) { |
| | | if (store.currentAngle == 3) { |
| | | viewInfoFrustum?.clear() |
| | | } else { |
| | | initCreateFrustum(deviceInfo, viewDroneInfo) |
| | | } |
| | | addFlyGltf( |
| | | viewDroneInfo.longitude, |
| | | viewDroneInfo.latitude, |
| | | deviceInfo.height, |
| | | deviceInfo.payloads[0], |
| | | deviceInfo.attitude_head, |
| | | deviceInfo, |
| | | dockInfo, |
| | | ssLinePath |
| | | ) |
| | | let domain = sessionStorage.getItem('domain') |
| | | if (domain !== '0') { |
| | | addFlyLaser( |
| | | deviceInfo.payloads[0] |
| | | ) |
| | | } |
| | | setelCamera( |
| | | viewDroneInfo.longitude, |
| | | viewDroneInfo.latitude, |
| | | deviceInfo.height, |
| | | deviceInfo.payloads[0], |
| | | deviceInfo.attitude_head, |
| | | ) |
| | | } else { |
| | | removeById('drone_fly') |
| | | droneEntity = null |
| | | |
| | | lastTime = null |
| | | viewInfoFrustum?.clear() |
| | | viewInfoFrustum = null |
| | | |
| | | window.cesiumViewer.camera.lookAtTransform(globalCesium.Matrix4.IDENTITY) |
| | | store.commit('SET_CURRENTANGLE', 1) |
| | | } |
| | | } else { |
| | | if (deviceInfo.mode_code && [3, 4, 5, 9, 10].includes(deviceInfo.mode_code)) { |
| | | initCreateFrustum(deviceInfo, viewDroneInfo) |
| | | addFlyGltf( |
| | | viewDroneInfo.longitude, |
| | | viewDroneInfo.latitude, |
| | | deviceInfo.height, |
| | | deviceInfo.payloads[0], |
| | | deviceInfo.attitude_head, |
| | | deviceInfo, |
| | | dockInfo, |
| | | ssLinePath |
| | | ) |
| | | let domain = sessionStorage.getItem('domain') |
| | | if (domain !== '0') { |
| | | addFlyLaser( |
| | | deviceInfo.payloads[0] |
| | | ) |
| | | } |
| | | } |
| | | } |
| | | const imgRightBlue = new URL('@/assets/images/arrow-right-pink.png', import.meta.url).href |
| | | // 无人机路线轨迹,判断是否存在drone_route_remove |
| | | if (getEntityById('drone_route_remove')) { |
| | | window.cesiumViewer.entities.add({ |
| | | name: 'drone_route_remove', |
| | | id: 'drone_route_remove', |
| | | polyline: { |
| | | width: 7, |
| | | positions: previousPositions, |
| | | material: new ImageTrailMaterial({ |
| | | color: globalCesium.Color.ORANGE, |
| | | speed: 10, |
| | | image: imgRightBlue, |
| | | repeat: { x: 1, y: 0 }, |
| | | }), |
| | | clampToGround: false, |
| | | }, |
| | | }) |
| | | } |
| | | |
| | | if (data.deviceInfo[data.currentSn].mode_code && [3, 4, 5, 9, 10].includes(deviceInfo.mode_code)) { |
| | | if (switchSaving) return (switchSaving = false) |
| | | // console.log('5555', data.deviceInfo[data.currentSn].longitude) |
| | | const newPositions = globalCesium.Cartesian3.fromDegrees( |
| | | data.deviceInfo[data.currentSn].longitude, |
| | | data.deviceInfo[data.currentSn].latitude, |
| | | Math.floor(viewDroneInfo.elevation), |
| | | ) |
| | | //若刷新,恢复无人机之前的路线轨迹 |
| | | const dataIndexedDB = await IndexedDBService.getDataById(wId) |
| | | if (previousPositions.length == 0 && dataIndexedDB) { |
| | | previousPositions = previousPositions.concat(dataIndexedDB?.temporaryflightData) |
| | | } else { |
| | | previousPositions = previousPositions.concat(newPositions) |
| | | } |
| | | // 保存无人机当前执行的路线,防止刷新数据丢失 |
| | | if (!dataIndexedDB) { |
| | | await IndexedDBService.addData({ |
| | | id: wId, |
| | | temporaryflightData: previousPositions, |
| | | }) |
| | | } else { |
| | | await IndexedDBService.updateData(wId, {temporaryflightData: previousPositions}) |
| | | } |
| | | // 更新线段的位置 |
| | | const drone_route_remove = getEntityById('drone_route_remove') |
| | | if (!drone_route_remove) return |
| | | drone_route_remove.polyline.positions = previousPositions |
| | | drone_route_remove.polyline._material._repeat._value.x = Math.floor(previousPositions.length / 7) |
| | | } else if ([0, 14].includes(deviceInfo.mode_code)) { |
| | | previousPositions = [] |
| | | await IndexedDBService.deleteData(wId) |
| | | const drone_route_remove = getEntityById('drone_route_remove') |
| | | if (!drone_route_remove) return |
| | | drone_route_remove.polyline.positions = previousPositions |
| | | removeById('drone_route_remove') |
| | | viewInfoFrustum?.clear() |
| | | viewInfoFrustum = null |
| | | } |
| | | } |
| | | if (data.currentType === EDeviceTypeName.Dock && data.dockInfo[data.currentSn]) { |
| | | if (!deviceInfo?.basic_osd?.longitude || !deviceInfo?.basic_osd?.latitude) return |
| | | console.log('6666', deviceInfo?.basic_osd?.longitude) |
| | | if (snCode.includes(data.currentSn)) return |
| | | snCode.push(data.currentSn) |
| | | } |
| | | } |
| | | |
| | | const imgLaser = new URL('@/assets/images/laser.png', import.meta.url).href |
| | | // 增加激光扫射点 |
| | | function addFlyLaser(payloads:any) { |
| | | const startPosition = globalCesium.Cartesian3.fromDegrees( |
| | | me_longitude, me_latitude, 0 |
| | | ) |
| | | const position = globalCesium.Cartesian3.fromDegrees( |
| | | payloads.measure_target_longitude, payloads.measure_target_latitude, payloads.measure_target_altitude |
| | | ) |
| | | measure_target_longitude = payloads.measure_target_longitude; |
| | | measure_target_latitude = payloads.measure_target_latitude; |
| | | measure_target_altitude = payloads.measure_target_altitude; |
| | | |
| | | if (laserEntity && laserEntity != null) { |
| | | laserEntity.position = new globalCesium.CallbackProperty(function () { |
| | | return position |
| | | }, false) |
| | | // laserEntity.orientation = new globalCesium.CallbackProperty(function () { |
| | | // return orientation |
| | | // }, false) |
| | | |
| | | laserEntity.show = true |
| | | |
| | | window.cesiumViewer.scene.requestRender() |
| | | } else { |
| | | laserEntity = window.cesiumViewer.entities.add({ |
| | | position, |
| | | id: 'laser_coordinate', |
| | | billboard: { |
| | | image: imgLaser, |
| | | outlineWidth: 0, |
| | | width: 36, |
| | | height: 36, |
| | | scale: 1.0, |
| | | } |
| | | }) |
| | | } |
| | | removeById('route_two_line') |
| | | twoLineEntity = window.cesiumViewer.entities.add({ |
| | | id: 'route_two_line', |
| | | polyline: { |
| | | positions: new globalCesium.CallbackProperty(() => [startPosition, position], false), |
| | | width: 5, |
| | | material: new globalCesium.PolylineDashMaterialProperty({ |
| | | color: globalCesium.Color.YELLOW, // 虚线颜色 |
| | | dashLength: 6.0, // 虚线段的长度 |
| | | }), |
| | | zIndex: 1, |
| | | clampToGround: false, |
| | | } |
| | | }) |
| | | // 增加激光扫射信息 |
| | | laserEntity.label = new globalCesium.LabelGraphics({ |
| | | text: `目标海拔:${Math.ceil(payloads.measure_target_altitude)}m\n测距距离:${Math.round( |
| | | Math.ceil(payloads.measure_target_distance), |
| | | )}m`, |
| | | font: '13px monospace', |
| | | showBackground: true, |
| | | horizontalOrigin: globalCesium.HorizontalOrigin.CENTER, |
| | | verticalOrigin: globalCesium.VerticalOrigin.BOTTOM, |
| | | disableDepthTestDistance: Number.POSITIVE_INFINITY, |
| | | pixelOffset: new globalCesium.Cartesian2(0, -40), |
| | | show: true, |
| | | }) |
| | | } |
| | | |
| | | function addFlyGltf (lng:any, lat:any, alt:any, payloads:any, attitude_head:any, deviceInfo:any, dockInfo:any, ssLinePath:any) { |
| | | const { gimbal_pitch, gimbal_yaw } = payloads |
| | | const position = globalCesium.Cartesian3.fromDegrees( |
| | | lng, lat, alt |
| | | ) |
| | | |
| | | const heading = globalCesium.Math.toRadians(Number(attitude_head)) //135度转弧度 |
| | | const pitch = globalCesium.Math.toRadians(0) |
| | | const roll = 0 |
| | | const hpr = new globalCesium.HeadingPitchRoll(heading, pitch, roll) |
| | | const orientation = globalCesium.Transforms.headingPitchRollQuaternion( |
| | | position, |
| | | hpr, |
| | | ) |
| | | |
| | | if (droneEntity && droneEntity != null) { |
| | | droneEntity.position = new globalCesium.CallbackProperty(function () { |
| | | return position |
| | | }, false) |
| | | |
| | | droneEntity.orientation = new globalCesium.CallbackProperty(function () { |
| | | return orientation |
| | | }, false) |
| | | |
| | | droneEntity.show = true |
| | | |
| | | window.cesiumViewer.scene.requestRender() |
| | | } else { |
| | | droneEntity = window.cesiumViewer.entities.add({ |
| | | show: true, |
| | | position, |
| | | id: 'drone_fly', |
| | | orientation: orientation, |
| | | model: { |
| | | uri: '/pilot-h5/gltf/dajiang-processed.gltf', //注意entitits.add方式加载gltf文件时,这里是uri,不是url,并且这种方式只能加载.glb格式的文件 |
| | | scale: 1, //缩放比例 |
| | | minimumPixelSize: 64, //最小像素大小,可以避免太小看不见 |
| | | maximumScale: 128, //模型的最大比例尺大小。minimumPixelSize的上限 |
| | | incrementallyLoadTextures: true, //加载模型后纹理是否可以继续流入 |
| | | runAnimations: true, //是否启动模型中制定的gltf动画 |
| | | clampAnimations: true, //制定gltf动画是否在没有关键帧的持续时间内保持最后一个姿势 |
| | | shadows: globalCesium.ShadowMode.ENABLED, |
| | | heightReference: globalCesium.HeightReference.NONE, |
| | | }, |
| | | }) |
| | | } |
| | | |
| | | // 创建无人机信息 |
| | | createDroneEntityLabel(droneEntity, deviceInfo, dockInfo, ssLinePath) |
| | | } |
| | | // 创建无人机信息 |
| | | async function createDroneEntityLabel (entity:any, deviceInfo:any, dockInfo:any, ssLinePath:any) { |
| | | deviceInfo = deviceInfo |
| | | if (currentTaskCreateTime === null) return |
| | | if (!deviceInfo?.longitude && !deviceInfo?.latitude) return |
| | | if (deviceInfo.mode_code !== 5) { |
| | | currentTaskCreateTime = null |
| | | return |
| | | } |
| | | const executingWayline = store.executingWayline |
| | | if (!executingWayline?.id) return |
| | | if (waylinePoints.length === 0) { |
| | | // 改成获取航线地址 |
| | | // const routeRes = await getRoutePlanningUrl( |
| | | // wId, |
| | | // executingWayline.id, |
| | | // ) |
| | | // const routeData = routeRes.data |
| | | // if (routeData.code !== 0) return this.$message.error('获取航线信息失败!') |
| | | waylinePoints = await getWaylineFilePoints(ssLinePath) |
| | | } |
| | | |
| | | const nextPoint = waylinePoints[currentWaypointIndex] |
| | | if (!nextPoint) return |
| | | // 两点之间的距离 |
| | | console.log('8888', deviceInfo.longitude) |
| | | const devicePosition = globalCesium.Cartesian3.fromDegrees( |
| | | Number(deviceInfo.longitude), |
| | | Number(deviceInfo.latitude), |
| | | 0, |
| | | ) |
| | | console.log('9999', nextPoint.longitude) |
| | | const targetPosition = globalCesium.Cartesian3.fromDegrees( |
| | | Number(nextPoint.longitude), |
| | | Number(nextPoint.latitude), |
| | | 0, |
| | | ) |
| | | // 距离下个点位的距离 |
| | | let distance = globalCesium.Cartesian3.distance( |
| | | devicePosition, |
| | | targetPosition, |
| | | ) |
| | | const dockPosition = { |
| | | longitude: dockInfo?.basic_osd?.longitude, |
| | | latitude: dockInfo?.basic_osd?.latitude, |
| | | } |
| | | |
| | | const distArr = waylinePoints.map((lnglat, index) => { |
| | | const { longitude, latitude } = lnglat |
| | | console.log('10000', longitude) |
| | | // 两点之间的距离 |
| | | let devicePosition = null |
| | | if (index === 0) { |
| | | devicePosition = globalCesium.Cartesian3.fromDegrees( |
| | | Number(dockPosition.longitude), |
| | | Number(dockPosition.latitude), |
| | | 0, |
| | | ) |
| | | } else { |
| | | console.log('110', prevPoint.longitude) |
| | | const prevPoint = waylinePoints[index - 1] |
| | | devicePosition = globalCesium.Cartesian3.fromDegrees( |
| | | Number(prevPoint.longitude), |
| | | Number(prevPoint.latitude), |
| | | 0, |
| | | ) |
| | | } |
| | | const targetPosition = globalCesium.Cartesian3.fromDegrees( |
| | | Number(longitude), |
| | | Number(latitude), |
| | | 0, |
| | | ) |
| | | let distance = globalCesium.Cartesian3.distance( |
| | | devicePosition, |
| | | targetPosition, |
| | | ) |
| | | return Math.round(distance) |
| | | }) |
| | | // 计算到达点位和之前点位的总距离 |
| | | let sum = null |
| | | if (distArr.length > 2) { |
| | | sum = distArr |
| | | .slice(0, currentWaypointIndex) |
| | | .reduce((accumulator, currentValue) => accumulator + currentValue, 0) |
| | | } else { |
| | | sum = distArr.reduce( |
| | | (accumulator, currentValue) => accumulator + currentValue, |
| | | 0, |
| | | ) |
| | | } |
| | | |
| | | if (sum == 0) { |
| | | sum = distArr[0] |
| | | } |
| | | |
| | | // 距离机场的位置 |
| | | const homeDistance = Math.floor(deviceInfo.home_distance) || 0 |
| | | // 速度 |
| | | let horizontalSpeed = deviceInfo.horizontal_speed || 0 |
| | | if (!droneSpeedArr.includes(horizontalSpeed) && horizontalSpeed > 1) { |
| | | droneSpeedArr.push(horizontalSpeed) |
| | | } |
| | | // 平均速度 |
| | | const averageSpeed = |
| | | droneSpeedArr.reduce((sum, current) => sum + current, 0) / |
| | | droneSpeedArr.length |
| | | // 已用时间 |
| | | let usedTime = Math.round((new Date().getTime() - currentTaskCreateTime) / 1000) - 30 |
| | | let throughDistance = usedTime * averageSpeed |
| | | |
| | | if (throughDistance < 0) { |
| | | throughDistance = 0 |
| | | usedTime = 0 |
| | | } |
| | | if (isNaN(throughDistance)) { |
| | | throughDistance = 0 |
| | | } |
| | | if (isNaN(usedTime)) { |
| | | usedTime = 0 |
| | | } |
| | | if (usedTime > 60) { |
| | | const minute = Math.floor(usedTime / 60) |
| | | const second = Math.round(usedTime % 60) |
| | | usedTime = `${minute}m${second}s` |
| | | } else { |
| | | usedTime = Math.round(usedTime) + 's' |
| | | } |
| | | |
| | | if (horizontalSpeed < 5) { |
| | | horizontalSpeed = 10 |
| | | } |
| | | // 预计到达下一个航点时间 |
| | | let arrivalTime = distance / horizontalSpeed |
| | | if (arrivalTime === Infinity || isNaN(arrivalTime)) { |
| | | arrivalTime = 0 |
| | | } |
| | | if (arrivalTime > 60) { |
| | | const minute = Math.floor(arrivalTime / 60) |
| | | const second = Math.round(arrivalTime % 60) |
| | | arrivalTime = `${minute}m${second}s` |
| | | } else { |
| | | arrivalTime = Math.round(arrivalTime) + 's' |
| | | } |
| | | entity.label = new globalCesium.LabelGraphics({ |
| | | text: `距离机场水平距离:${homeDistance}m\n距离下一个航点:${Math.round( |
| | | distance, |
| | | )}m\n预计到达下一航点时间:${arrivalTime}\n本次飞行时间:${usedTime}\n本次航线平面里程:${Math.round( |
| | | throughDistance, |
| | | )}m`, |
| | | font: '13px monospace', |
| | | showBackground: true, |
| | | horizontalOrigin: globalCesium.HorizontalOrigin.CENTER, |
| | | verticalOrigin: globalCesium.VerticalOrigin.BOTTOM, |
| | | disableDepthTestDistance: Number.POSITIVE_INFINITY, |
| | | pixelOffset: new globalCesium.Cartesian2(0, -40), |
| | | show: true, |
| | | }) |
| | | } |
| | | async function getWaylineFilePoints(fileUrl:any) { |
| | | const fileRes = await analyzeKmzFile(fileUrl); |
| | | const waylineContent = await fileRes['fileInfoObj']?.['wpmz/waylines.wpml']; |
| | | const xml = XMLToJSON(waylineContent); |
| | | const placemarkArr = xml?.Document?.Folder.Placemark; |
| | | const points = placemarkArr.map((placemark) => { |
| | | const lnglat = placemark.Point.coordinates?.['#text'].split(','); |
| | | return { |
| | | longitude: lnglat[0], |
| | | latitude: lnglat[1], |
| | | }; |
| | | }); |
| | | return points; |
| | | } |
| | | |
| | | function setelCamera (lng, lat, alt, payloads, attitude_head) { |
| | | const { gimbal_pitch, gimbal_yaw } = payloads |
| | | let time = 0 |
| | | |
| | | let current = store.currentAngle |
| | | var heading = globalCesium.Math.toRadians(Number(gimbal_yaw || attitude_head)) // 相机左右方向 |
| | | |
| | | if (current == 1) { |
| | | lastTime = null |
| | | droneEntity.show = true |
| | | window.cesiumViewer.camera.lookAtTransform(globalCesium.Matrix4.IDENTITY) |
| | | } else if (current == 2) { |
| | | lastTime = null |
| | | droneEntity.show = true |
| | | window.cesiumViewer.camera.lookAtTransform(globalCesium.Matrix4.IDENTITY) |
| | | |
| | | let center = globalCesium.Cartesian3.fromDegrees(lng, lat) |
| | | |
| | | window.cesiumViewer.camera.lookAt(center, new globalCesium.Cartesian3(0.0, 0, 1500)) |
| | | } else if (current == 3) { |
| | | droneEntity.show = false |
| | | |
| | | let curTime = Date.now() |
| | | if (lastTime && lastTime != null) { |
| | | time = (curTime - lastTime) / 1000 |
| | | } |
| | | |
| | | window.cesiumViewer.camera.lookAtTransform(globalCesium.Matrix4.IDENTITY) |
| | | let center = globalCesium.Cartesian3.fromDegrees(lng, lat, alt) |
| | | var pitch = globalCesium.Math.toRadians(pitchAngle({ gimbal_pitch })) // 相机上下方向,负值表示向下看 |
| | | |
| | | window.cesiumViewer.camera.flyTo({ |
| | | destination: center, |
| | | orientation: { |
| | | heading, |
| | | pitch, |
| | | roll: 0 |
| | | }, |
| | | duration: time, |
| | | complete () { |
| | | window.cesiumViewer.camera.lookAt(center, new globalCesium.HeadingPitchRange( |
| | | heading, |
| | | pitch, |
| | | 0.01 |
| | | )) |
| | | } |
| | | }) |
| | | |
| | | lastTime = Date.now() |
| | | } |
| | | } |
| | | // pitch |
| | | function pitchAngle (options) { |
| | | const gimbal_pitch = Number(options?.gimbal_pitch) || 0 |
| | | return gimbal_pitch |
| | | } |
| | | // 创建视锥 |
| | | function initCreateFrustum (deviceInfo:any, test:any) { |
| | | |
| | | if (!deviceInfo || !deviceInfo.longitude || !deviceInfo.latitude) return |
| | | |
| | | const attitude_head = 180 + deviceInfo.attitude_head |
| | | |
| | | const gimbal_pitch = 90 - pitchAngle({ gimbal_pitch: deviceInfo.payloads[0].gimbal_pitch }) |
| | | |
| | | viewInfoFrustum?.clear() |
| | | viewInfoFrustum = new CreateFrustum(window.cesiumViewer, { |
| | | position: { |
| | | longitude: test.longitude, |
| | | latitude: test.latitude, |
| | | altitude: deviceInfo.height, |
| | | }, |
| | | width: 30, |
| | | height: 30, |
| | | fov: 20.0, |
| | | near: 3.0, |
| | | far: 250.0, |
| | | roll: gimbal_pitch, |
| | | pitch: 0, |
| | | heading: attitude_head, |
| | | }) |
| | | } |
| | | |
| | | // 获取手机定位和激光发射定位的高度和距离 |
| | | function getTwoHeightWidth(workspaceId:String) { |
| | | // 国内地图不会为0或者小于0 |
| | | console.log('自身定位经纬度1', me_latitude) |
| | | console.log('自身定位经纬度2', me_longitude) |
| | | console.log('激光扫过定位1', measure_target_latitude) |
| | | console.log('激光扫过定位2', measure_target_longitude) |
| | | if (me_latitude <= 0 || me_longitude <= 0 || measure_target_latitude <= 0 || measure_target_longitude <= 0) return |
| | | getTwoPointInfo(workspaceId,me_latitude,me_longitude,measure_target_latitude,measure_target_longitude).then((res:any) => { |
| | | const heightDifference = res.data.heightDifference |
| | | const distance = res.data.distance |
| | | // 增加激光扫射信息 |
| | | myAppEntity.label = new globalCesium.LabelGraphics({ |
| | | text: `高度:${Math.ceil(heightDifference)}m\n距离:${Math.round( |
| | | Math.ceil(distance), |
| | | )}m`, |
| | | font: '13px monospace', |
| | | showBackground: true, |
| | | horizontalOrigin: globalCesium.HorizontalOrigin.CENTER, |
| | | verticalOrigin: globalCesium.VerticalOrigin.BOTTOM, |
| | | disableDepthTestDistance: Number.POSITIVE_INFINITY, |
| | | pixelOffset: new globalCesium.Cartesian2(0, -40), |
| | | show: true, |
| | | }) |
| | | }) |
| | | } |
| | | |
| | | const meMapSrc = new URL('@/assets/images/me.png', import.meta.url).href |
| | | // 获取手机设备定位 |
| | | function getPhoneLocation(value:Boolean, workspaceId:String) { |
| | | if (window.cesiumViewer == null) return |
| | | // 创建一个箭头实体 |
| | | removeById('me_dw') |
| | | myAppEntity = window.cesiumViewer.entities.add({ |
| | | id: 'me_dw', |
| | | position: globalCesium.Cartesian3.fromDegrees(0, 0, 0), // 初始位置 |
| | | billboard: { |
| | | image: meMapSrc, // 箭头图片路径 |
| | | width: 30, |
| | | height: 30, |
| | | alignedAxis: globalCesium.Cartesian3.UNIT_Z, // 对齐轴 |
| | | }, |
| | | // orientation: new globalCesium.CallbackProperty(() => { |
| | | // return globalCesium.Transforms.headingPitchRollQuaternion( |
| | | // myAppEntity.position.getValue(window.cesiumViewer.clock.currentTime), |
| | | // new globalCesium.HeadingPitchRoll(heading, 0, 0) // 设置朝向 |
| | | // ); |
| | | // }, false), |
| | | }); |
| | | |
| | | let heading = 0; // 初始朝向 |
| | | // 获取设备朝向 |
| | | // if (window.DeviceOrientationEvent) { |
| | | // window.addEventListener('deviceorientation', (event) => { |
| | | // if (event.alpha !== null) { |
| | | // heading = globalCesium.Math.toRadians(event.alpha); // 将 alpha 转换为弧度 |
| | | // } |
| | | // }); |
| | | // } else { |
| | | // console.log('手机不支持.'); |
| | | // } |
| | | |
| | | // 获取设备位置 |
| | | if (navigator.geolocation) { |
| | | navigator.geolocation.watchPosition((position) => { |
| | | me_longitude = position.coords.longitude; |
| | | me_latitude = position.coords.latitude; |
| | | me_altitude = position.coords.altitude || 0; |
| | | |
| | | // 更新箭头实体的位置 |
| | | myAppEntity.position = globalCesium.Cartesian3.fromDegrees(me_longitude, me_latitude, me_altitude); |
| | | if (value) { |
| | | // 将相机定位到设备位置 只有点击图标才可以执行这里 |
| | | window.cesiumViewer.camera.setView({ |
| | | destination: globalCesium.Cartesian3.fromDegrees(me_longitude, me_latitude, me_altitude + 1000), // 相机高度 |
| | | // orientation: { |
| | | // heading: heading, // 相机朝向 |
| | | // pitch: globalCesium.Math.toRadians(-90), // 俯视角度 |
| | | // roll: 0, |
| | | // }, |
| | | }); |
| | | } |
| | | // 查看两条线直接距离和高度 |
| | | getTwoHeightWidth(workspaceId) |
| | | }, (error) => { |
| | | console.error('定位有误:', error); |
| | | }); |
| | | } else { |
| | | console.log('手机不支持.'); |
| | | } |
| | | } |
| | | |
| | | |
| | | return { |
| | | initDock, |
| | | getPhoneLocation, |
| | | } |
| | | } |
| | |
| | | let viewInfoFrustum = null |
| | | |
| | | // 手机设备定位 |
| | | let me_latitude = 0; |
| | | let me_longitude = 0; |
| | | let me_latitude = 0;// 28.624647955787974; |
| | | let me_longitude = 0;//115.85635808986208; |
| | | let me_altitude = 0; |
| | | // 扫射定位 |
| | | let measure_target_longitude = 0; |
| | | let measure_target_latitude = 0; |
| | | let measure_target_longitude = 0;//115.85635872364763; |
| | | let measure_target_latitude = 0;//28.624648387113627; |
| | | let measure_target_altitude = 0; |
| | | |
| | | |
| | | async function initDock(data:any, sn:any, dockSn:any, workspaceId:any, ssLinePath:any, viewDroneInfo:any) { |
| | | // console.log('globalCesium查看是', globalCesium) |
| | | // 获取workspaceId |
| | | wId = workspaceId |
| | | // 给无人机参数赋值 |
| | |
| | | // 机场信息 |
| | | dockInfo = data.dockInfo[`${dockSn}`] |
| | | viewDroneInfo = viewDroneInfo |
| | | console.log('导致地图不显示原因1',viewDroneInfo.longitude) |
| | | // 当前飞行任务创建时间 |
| | | if (data.currentType === EDeviceTypeName.Aircraft && deviceInfo) { |
| | | const droneLngLat = { |
| | |
| | | if (store.currentAngle == 3) { |
| | | viewInfoFrustum?.clear() |
| | | } else { |
| | | // 暂时把视锥代码注释掉 |
| | | initCreateFrustum(deviceInfo, viewDroneInfo) |
| | | } |
| | | addFlyGltf( |
| | |
| | | } |
| | | } else { |
| | | if (deviceInfo.mode_code && [3, 4, 5, 9, 10].includes(deviceInfo.mode_code)) { |
| | | // 暂时把视锥代码注释掉 |
| | | initCreateFrustum(deviceInfo, viewDroneInfo) |
| | | addFlyGltf( |
| | | viewDroneInfo.longitude, |
| | |
| | | } |
| | | } |
| | | const imgRightBlue = new URL('@/assets/images/arrow-right-pink.png', import.meta.url).href |
| | | // 暂时把航线屏蔽 |
| | | // 无人机路线轨迹,判断是否存在drone_route_remove |
| | | if (getEntityById('drone_route_remove')) { |
| | | window.cesiumViewer.entities.add({ |
| | | name: 'drone_route_remove', |
| | | id: 'drone_route_remove', |
| | | polyline: { |
| | | width: 7, |
| | | positions: previousPositions, |
| | | material: new ImageTrailMaterial({ |
| | | color: globalCesium.Color.ORANGE, |
| | | speed: 10, |
| | | image: imgRightBlue, |
| | | repeat: { x: 1, y: 0 }, |
| | | }), |
| | | clampToGround: false, |
| | | }, |
| | | }) |
| | | } |
| | | // if (getEntityById('drone_route_remove')) { |
| | | // window.cesiumViewer.entities.add({ |
| | | // name: 'drone_route_remove', |
| | | // id: 'drone_route_remove', |
| | | // polyline: { |
| | | // width: 7, |
| | | // positions: previousPositions, |
| | | // material: new ImageTrailMaterial({ |
| | | // color: globalCesium.Color.ORANGE, |
| | | // speed: 10, |
| | | // image: imgRightBlue, |
| | | // repeat: { x: 1, y: 0 }, |
| | | // }), |
| | | // clampToGround: false, |
| | | // }, |
| | | // }) |
| | | // } |
| | | |
| | | if (data.deviceInfo[data.currentSn].mode_code && [3, 4, 5, 9, 10].includes(deviceInfo.mode_code)) { |
| | | if (switchSaving) return (switchSaving = false) |
| | | // console.log('5555', data.deviceInfo[data.currentSn].longitude) |
| | | const newPositions = globalCesium.Cartesian3.fromDegrees( |
| | | data.deviceInfo[data.currentSn].longitude, |
| | | data.deviceInfo[data.currentSn].latitude, |
| | | Math.floor(viewDroneInfo.elevation), |
| | | ) |
| | | //若刷新,恢复无人机之前的路线轨迹 |
| | | const dataIndexedDB = await IndexedDBService.getDataById(wId) |
| | | if (previousPositions.length == 0 && dataIndexedDB) { |
| | | previousPositions = previousPositions.concat(dataIndexedDB?.temporaryflightData) |
| | | } else { |
| | | previousPositions = previousPositions.concat(newPositions) |
| | | } |
| | | // 保存无人机当前执行的路线,防止刷新数据丢失 |
| | | if (!dataIndexedDB) { |
| | | await IndexedDBService.addData({ |
| | | id: wId, |
| | | temporaryflightData: previousPositions, |
| | | }) |
| | | } else { |
| | | await IndexedDBService.updateData(wId, {temporaryflightData: previousPositions}) |
| | | } |
| | | // 更新线段的位置 |
| | | const drone_route_remove = getEntityById('drone_route_remove') |
| | | if (!drone_route_remove) return |
| | | drone_route_remove.polyline.positions = previousPositions |
| | | drone_route_remove.polyline._material._repeat._value.x = Math.floor(previousPositions.length / 7) |
| | | } else if ([0, 14].includes(deviceInfo.mode_code)) { |
| | | previousPositions = [] |
| | | await IndexedDBService.deleteData(wId) |
| | | const drone_route_remove = getEntityById('drone_route_remove') |
| | | if (!drone_route_remove) return |
| | | drone_route_remove.polyline.positions = previousPositions |
| | | removeById('drone_route_remove') |
| | | viewInfoFrustum?.clear() |
| | | viewInfoFrustum = null |
| | | } |
| | | // if (data.deviceInfo[data.currentSn].mode_code && [3, 4, 5, 9, 10].includes(deviceInfo.mode_code)) { |
| | | // if (switchSaving) return (switchSaving = false) |
| | | // // console.log('5555', data.deviceInfo[data.currentSn].longitude) |
| | | // const newPositions = globalCesium.Cartesian3.fromDegrees( |
| | | // data.deviceInfo[data.currentSn].longitude, |
| | | // data.deviceInfo[data.currentSn].latitude, |
| | | // Math.floor(viewDroneInfo.elevation), |
| | | // ) |
| | | // //若刷新,恢复无人机之前的路线轨迹 |
| | | // const dataIndexedDB = await IndexedDBService.getDataById(wId) |
| | | // if (previousPositions.length == 0 && dataIndexedDB) { |
| | | // previousPositions = previousPositions.concat(dataIndexedDB?.temporaryflightData) |
| | | // } else { |
| | | // previousPositions = previousPositions.concat(newPositions) |
| | | // } |
| | | // // 保存无人机当前执行的路线,防止刷新数据丢失 |
| | | // if (!dataIndexedDB) { |
| | | // await IndexedDBService.addData({ |
| | | // id: wId, |
| | | // temporaryflightData: previousPositions, |
| | | // }) |
| | | // } else { |
| | | // await IndexedDBService.updateData(wId, {temporaryflightData: previousPositions}) |
| | | // } |
| | | // // 更新线段的位置 |
| | | // const drone_route_remove = getEntityById('drone_route_remove') |
| | | // if (!drone_route_remove) return |
| | | // drone_route_remove.polyline.positions = previousPositions |
| | | // drone_route_remove.polyline._material._repeat._value.x = Math.floor(previousPositions.length / 7) |
| | | // } else if ([0, 14].includes(deviceInfo.mode_code)) { |
| | | // previousPositions = [] |
| | | // await IndexedDBService.deleteData(wId) |
| | | // const drone_route_remove = getEntityById('drone_route_remove') |
| | | // if (!drone_route_remove) return |
| | | // drone_route_remove.polyline.positions = previousPositions |
| | | // removeById('drone_route_remove') |
| | | // viewInfoFrustum?.clear() |
| | | // viewInfoFrustum = null |
| | | // } |
| | | } |
| | | if (data.currentType === EDeviceTypeName.Dock && data.dockInfo[data.currentSn]) { |
| | | if (!deviceInfo?.basic_osd?.longitude || !deviceInfo?.basic_osd?.latitude) return |
| | | console.log('6666', deviceInfo?.basic_osd?.longitude) |
| | | if (snCode.includes(data.currentSn)) return |
| | | snCode.push(data.currentSn) |
| | | } |
| | |
| | | const imgLaser = new URL('@/assets/images/laser.png', import.meta.url).href |
| | | // 增加激光扫射点 |
| | | function addFlyLaser(payloads:any) { |
| | | console.log('激光扫射经度',payloads.measure_target_longitude) |
| | | console.log('激光扫射纬度',payloads.measure_target_latitude) |
| | | const startPosition = globalCesium.Cartesian3.fromDegrees( |
| | | me_longitude, me_latitude, 0 |
| | | ) |
| | |
| | | measure_target_latitude = payloads.measure_target_latitude; |
| | | measure_target_altitude = payloads.measure_target_altitude; |
| | | |
| | | if (laserEntity && laserEntity != null) { |
| | | laserEntity.position = new globalCesium.CallbackProperty(function () { |
| | | return position |
| | | }, false) |
| | | // laserEntity.orientation = new globalCesium.CallbackProperty(function () { |
| | | // return orientation |
| | | // if (laserEntity && laserEntity != null) { |
| | | // laserEntity.position = new globalCesium.CallbackProperty(function () { |
| | | // return position |
| | | // }, false) |
| | | // // laserEntity.orientation = new globalCesium.CallbackProperty(function () { |
| | | // // return orientation |
| | | // // }, false) |
| | | |
| | | laserEntity.show = true |
| | | // laserEntity.show = true |
| | | |
| | | window.cesiumViewer.scene.requestRender() |
| | | } else { |
| | | // window.cesiumViewer.scene.requestRender() |
| | | // } else { |
| | | removeById('laser_coordinate') |
| | | laserEntity = window.cesiumViewer.entities.add({ |
| | | position, |
| | | position: globalCesium.Cartesian3.fromDegrees(payloads.measure_target_longitude, payloads.measure_target_latitude, payloads.measure_target_altitude), |
| | | id: 'laser_coordinate', |
| | | billboard: { |
| | | image: imgLaser, |
| | | outlineWidth: 0, |
| | | width: 36, |
| | | height: 36, |
| | | width: 30, |
| | | height: 30, |
| | | scale: 1.0, |
| | | outlineWidth: 0, |
| | | } |
| | | }) |
| | | } |
| | | // } |
| | | // 增加激光扫射信息 |
| | | laserEntity.label = new globalCesium.LabelGraphics({ |
| | | text: `H:${Math.ceil(payloads.measure_target_altitude)}m-L:${Math.round(Math.ceil(payloads.measure_target_distance),)}m`, |
| | | font: '13px monospace', |
| | | fillColor: globalCesium.Color.fromCssColorString('red'), // 文字颜色 |
| | | backgroundColor: globalCesium.Color.WHITE, // 背景颜色 |
| | | showBackground: true, |
| | | horizontalOrigin: globalCesium.HorizontalOrigin.CENTER, |
| | | verticalOrigin: globalCesium.VerticalOrigin.BOTTOM, |
| | | disableDepthTestDistance: Number.POSITIVE_INFINITY, |
| | | pixelOffset: new globalCesium.Cartesian2(0, -20), |
| | | show: true, |
| | | }) |
| | | console.log('起点',startPosition) |
| | | console.log('终点',position) |
| | | if (startPosition && position) { |
| | | removeById('route_two_line') |
| | | twoLineEntity = window.cesiumViewer.entities.add({ |
| | | id: 'route_two_line', |
| | | polyline: { |
| | | positions: new globalCesium.CallbackProperty(() => [startPosition, position], false), |
| | | width: 5, |
| | | material: new globalCesium.PolylineDashMaterialProperty({ |
| | | color: globalCesium.Color.YELLOW, // 虚线颜色 |
| | | dashLength: 6.0, // 虚线段的长度 |
| | | }), |
| | | zIndex: 1, |
| | | width: 3, |
| | | material: globalCesium.Color.RED, |
| | | zIndex: -1, |
| | | clampToGround: false, |
| | | } |
| | | }) |
| | | // 增加激光扫射信息 |
| | | laserEntity.label = new globalCesium.LabelGraphics({ |
| | | text: `目标海拔:${Math.ceil(payloads.measure_target_altitude)}m\n测距距离:${Math.round( |
| | | Math.ceil(payloads.measure_target_distance), |
| | | )}m`, |
| | | font: '13px monospace', |
| | | showBackground: true, |
| | | horizontalOrigin: globalCesium.HorizontalOrigin.CENTER, |
| | | verticalOrigin: globalCesium.VerticalOrigin.BOTTOM, |
| | | disableDepthTestDistance: Number.POSITIVE_INFINITY, |
| | | pixelOffset: new globalCesium.Cartesian2(0, -40), |
| | | show: true, |
| | | }) |
| | | } |
| | | } |
| | | |
| | | function addFlyGltf (lng:any, lat:any, alt:any, payloads:any, attitude_head:any, deviceInfo:any, dockInfo:any, ssLinePath:any) { |
| | |
| | | } |
| | | |
| | | // 创建无人机信息 |
| | | createDroneEntityLabel(droneEntity, deviceInfo, dockInfo, ssLinePath) |
| | | } |
| | | // 创建无人机信息 |
| | | async function createDroneEntityLabel (entity:any, deviceInfo:any, dockInfo:any, ssLinePath:any) { |
| | | deviceInfo = deviceInfo |
| | | if (currentTaskCreateTime === null) return |
| | | if (!deviceInfo?.longitude && !deviceInfo?.latitude) return |
| | | if (deviceInfo.mode_code !== 5) { |
| | | currentTaskCreateTime = null |
| | | return |
| | | } |
| | | const executingWayline = store.executingWayline |
| | | if (!executingWayline?.id) return |
| | | if (waylinePoints.length === 0) { |
| | | // 改成获取航线地址 |
| | | // const routeRes = await getRoutePlanningUrl( |
| | | // wId, |
| | | // executingWayline.id, |
| | | // ) |
| | | // const routeData = routeRes.data |
| | | // if (routeData.code !== 0) return this.$message.error('获取航线信息失败!') |
| | | waylinePoints = await getWaylineFilePoints(ssLinePath) |
| | | } |
| | | |
| | | const nextPoint = waylinePoints[currentWaypointIndex] |
| | | if (!nextPoint) return |
| | | // 两点之间的距离 |
| | | console.log('8888', deviceInfo.longitude) |
| | | const devicePosition = globalCesium.Cartesian3.fromDegrees( |
| | | Number(deviceInfo.longitude), |
| | | Number(deviceInfo.latitude), |
| | | 0, |
| | | ) |
| | | console.log('9999', nextPoint.longitude) |
| | | const targetPosition = globalCesium.Cartesian3.fromDegrees( |
| | | Number(nextPoint.longitude), |
| | | Number(nextPoint.latitude), |
| | | 0, |
| | | ) |
| | | // 距离下个点位的距离 |
| | | let distance = globalCesium.Cartesian3.distance( |
| | | devicePosition, |
| | | targetPosition, |
| | | ) |
| | | const dockPosition = { |
| | | longitude: dockInfo?.basic_osd?.longitude, |
| | | latitude: dockInfo?.basic_osd?.latitude, |
| | | } |
| | | |
| | | const distArr = waylinePoints.map((lnglat, index) => { |
| | | const { longitude, latitude } = lnglat |
| | | console.log('10000', longitude) |
| | | // 两点之间的距离 |
| | | let devicePosition = null |
| | | if (index === 0) { |
| | | devicePosition = globalCesium.Cartesian3.fromDegrees( |
| | | Number(dockPosition.longitude), |
| | | Number(dockPosition.latitude), |
| | | 0, |
| | | ) |
| | | } else { |
| | | console.log('110', prevPoint.longitude) |
| | | const prevPoint = waylinePoints[index - 1] |
| | | devicePosition = globalCesium.Cartesian3.fromDegrees( |
| | | Number(prevPoint.longitude), |
| | | Number(prevPoint.latitude), |
| | | 0, |
| | | ) |
| | | } |
| | | const targetPosition = globalCesium.Cartesian3.fromDegrees( |
| | | Number(longitude), |
| | | Number(latitude), |
| | | 0, |
| | | ) |
| | | let distance = globalCesium.Cartesian3.distance( |
| | | devicePosition, |
| | | targetPosition, |
| | | ) |
| | | return Math.round(distance) |
| | | }) |
| | | // 计算到达点位和之前点位的总距离 |
| | | let sum = null |
| | | if (distArr.length > 2) { |
| | | sum = distArr |
| | | .slice(0, currentWaypointIndex) |
| | | .reduce((accumulator, currentValue) => accumulator + currentValue, 0) |
| | | } else { |
| | | sum = distArr.reduce( |
| | | (accumulator, currentValue) => accumulator + currentValue, |
| | | 0, |
| | | ) |
| | | } |
| | | |
| | | if (sum == 0) { |
| | | sum = distArr[0] |
| | | } |
| | | |
| | | // 距离机场的位置 |
| | | const homeDistance = Math.floor(deviceInfo.home_distance) || 0 |
| | | // 速度 |
| | | let horizontalSpeed = deviceInfo.horizontal_speed || 0 |
| | | if (!droneSpeedArr.includes(horizontalSpeed) && horizontalSpeed > 1) { |
| | | droneSpeedArr.push(horizontalSpeed) |
| | | } |
| | | // 平均速度 |
| | | const averageSpeed = |
| | | droneSpeedArr.reduce((sum, current) => sum + current, 0) / |
| | | droneSpeedArr.length |
| | | // 已用时间 |
| | | let usedTime = Math.round((new Date().getTime() - currentTaskCreateTime) / 1000) - 30 |
| | | let throughDistance = usedTime * averageSpeed |
| | | |
| | | if (throughDistance < 0) { |
| | | throughDistance = 0 |
| | | usedTime = 0 |
| | | } |
| | | if (isNaN(throughDistance)) { |
| | | throughDistance = 0 |
| | | } |
| | | if (isNaN(usedTime)) { |
| | | usedTime = 0 |
| | | } |
| | | if (usedTime > 60) { |
| | | const minute = Math.floor(usedTime / 60) |
| | | const second = Math.round(usedTime % 60) |
| | | usedTime = `${minute}m${second}s` |
| | | } else { |
| | | usedTime = Math.round(usedTime) + 's' |
| | | } |
| | | |
| | | if (horizontalSpeed < 5) { |
| | | horizontalSpeed = 10 |
| | | } |
| | | // 预计到达下一个航点时间 |
| | | let arrivalTime = distance / horizontalSpeed |
| | | if (arrivalTime === Infinity || isNaN(arrivalTime)) { |
| | | arrivalTime = 0 |
| | | } |
| | | if (arrivalTime > 60) { |
| | | const minute = Math.floor(arrivalTime / 60) |
| | | const second = Math.round(arrivalTime % 60) |
| | | arrivalTime = `${minute}m${second}s` |
| | | } else { |
| | | arrivalTime = Math.round(arrivalTime) + 's' |
| | | } |
| | | entity.label = new globalCesium.LabelGraphics({ |
| | | text: `距离机场水平距离:${homeDistance}m\n距离下一个航点:${Math.round( |
| | | distance, |
| | | )}m\n预计到达下一航点时间:${arrivalTime}\n本次飞行时间:${usedTime}\n本次航线平面里程:${Math.round( |
| | | throughDistance, |
| | | )}m`, |
| | | font: '13px monospace', |
| | | showBackground: true, |
| | | horizontalOrigin: globalCesium.HorizontalOrigin.CENTER, |
| | | verticalOrigin: globalCesium.VerticalOrigin.BOTTOM, |
| | | disableDepthTestDistance: Number.POSITIVE_INFINITY, |
| | | pixelOffset: new globalCesium.Cartesian2(0, -40), |
| | | show: true, |
| | | }) |
| | | } |
| | | async function getWaylineFilePoints(fileUrl:any) { |
| | | const fileRes = await analyzeKmzFile(fileUrl); |
| | | const waylineContent = await fileRes['fileInfoObj']?.['wpmz/waylines.wpml']; |
| | | const xml = XMLToJSON(waylineContent); |
| | | const placemarkArr = xml?.Document?.Folder.Placemark; |
| | | const points = placemarkArr.map((placemark) => { |
| | | const lnglat = placemark.Point.coordinates?.['#text'].split(','); |
| | | return { |
| | | longitude: lnglat[0], |
| | | latitude: lnglat[1], |
| | | }; |
| | | }); |
| | | return points; |
| | | // createDroneEntityLabel(droneEntity, deviceInfo, dockInfo, ssLinePath) |
| | | } |
| | | |
| | | function setelCamera (lng, lat, alt, payloads, attitude_head) { |
| | |
| | | } |
| | | // 创建视锥 |
| | | function initCreateFrustum (deviceInfo:any, test:any) { |
| | | |
| | | if (!deviceInfo || !deviceInfo.longitude || !deviceInfo.latitude) return |
| | | // console.log('视锥传参1',deviceInfo) |
| | | // console.log('视锥传参2',test) |
| | | if (!deviceInfo || !deviceInfo.longitude || !deviceInfo.latitude || !test.longitude || !test.latitude) return |
| | | |
| | | const attitude_head = 180 + deviceInfo.attitude_head |
| | | |
| | |
| | | |
| | | // 获取手机定位和激光发射定位的高度和距离 |
| | | function getTwoHeightWidth(workspaceId:String) { |
| | | // 国内地图不会为0或者小于0 |
| | | console.log('自身定位经纬度1', me_latitude) |
| | | console.log('自身定位经纬度2', me_longitude) |
| | | console.log('激光扫过定位1', measure_target_latitude) |
| | | console.log('激光扫过定位2', measure_target_longitude) |
| | | if (me_latitude <= 0 || me_longitude <= 0 || measure_target_latitude <= 0 || measure_target_longitude <= 0) return |
| | | getTwoPointInfo(workspaceId,me_latitude,me_longitude,measure_target_latitude,measure_target_longitude).then((res:any) => { |
| | | const heightDifference = res.data.heightDifference |
| | | const distance = res.data.distance |
| | | // 增加激光扫射信息 |
| | | myAppEntity.label = new globalCesium.LabelGraphics({ |
| | | text: `高度:${Math.ceil(heightDifference)}m\n距离:${Math.round( |
| | | Math.ceil(distance), |
| | | )}m`, |
| | | text: `H:${Math.ceil(heightDifference)}m-L:${Math.round(Math.ceil(distance),)}m`, |
| | | font: '13px monospace', |
| | | fillColor: globalCesium.Color.fromCssColorString('red'), // 文字颜色 |
| | | backgroundColor: globalCesium.Color.WHITE, // 背景颜色 |
| | | showBackground: true, |
| | | horizontalOrigin: globalCesium.HorizontalOrigin.CENTER, |
| | | verticalOrigin: globalCesium.VerticalOrigin.BOTTOM, |
| | | disableDepthTestDistance: Number.POSITIVE_INFINITY, |
| | | pixelOffset: new globalCesium.Cartesian2(0, -40), |
| | | pixelOffset: new globalCesium.Cartesian2(0, -24), |
| | | show: true, |
| | | }) |
| | | }) |
| | |
| | | if (window.cesiumViewer == null) return |
| | | if (navigator.geolocation) { |
| | | navigator.geolocation.watchPosition((position) => { |
| | | console.log(me_longitude,position.coords.longitude) |
| | | console.log(me_latitude,position.coords.latitude) |
| | | if (me_longitude > 0 && me_longitude == position.coords.longitude && me_latitude > 0 && me_latitude == position.coords.latitude) return |
| | | me_longitude = position.coords.longitude; |
| | | me_latitude = position.coords.latitude; |
| | |
| | | width: 30, |
| | | height: 30, |
| | | scale: 1.0, |
| | | outlineWidth: 0, |
| | | } |
| | | }); |
| | | // 查看两条线直接距离和高度 |