<template> <div class="init-speed-track"> <EasyMapComponent class="map" :showBaseSwitch="true" @easyMapLoad="mapLoad" /> <div class="track"> <el-card shadow="always"> <template #header> <div class="card-header"> <span>轨迹列表</span> <el-button-group> <template v-for="[key, value] in SourceMap"> <el-button :color="value.color" size="small" @click="drawTrack(key)" style="color: white">{{value.label}}</el-button> </template> </el-button-group> </div> </template> <div class="track-line"> <template v-for="(item, index) in trackList"> <div class="line"> <div class="label" :style="`color: ${SourceMap.get(item.type).color};`">{{SourceMap.get(item.type).label}}</div> <el-tooltip :enterable="false" placement="top" content="隐藏" v-if="item.show"> <img class="__hover" src="./ship-track-visible.svg" @click="handleShow(false, item)"/> </el-tooltip> <el-tooltip :enterable="false" placement="top" content="显示" v-else> <img class="__hover" src="./ship-track-invisible.svg" @click="handleShow(true, item)"/> </el-tooltip> </div> </template> </div> </el-card> <el-card shadow="always"> <template #header> <div class="card-header"> <span>轨迹点列表</span> <el-button v-if="trackPointList.length > 0" type="primary" size="small" @click="onSubmit" style="color: white">保存</el-button> </div> </template> <div class="track-point"> <template v-for="(item, index) in trackPointList"> <div class="point"> <div class="position"> <span>{{item.position[0]}}</span><br/> <span>{{item.position[1]}}</span> </div> <div class="speed"> <el-input-number v-model="item.speed" :precision="2" :step="0.1" :max="100" :min="0" @focus="onPointFocus(trackPointList[index - 1], item, trackPointList[index + 1])"/> </div> </div> </template> </div> </el-card> </div> </div> </template> <script lang="ts"> import { defineComponent, ref, nextTick, onMounted, watch, computed, ComponentInternalInstance, reactive, toRefs, getCurrentInstance } from 'vue' import {useStore} from 'vuex' import * as source from "ol/source"; import * as layer from "ol/layer"; import * as style from "ol/style"; import * as ol from "ol"; import * as sphere from "ol/sphere"; import * as interaction from "ol/interaction"; import {createBox} from "ol/interaction/Draw"; import {unByKey} from "ol/Observable"; import { Geometry } from 'ol/geom'; import { EventsKey } from 'ol/events'; import { Coordinate } from 'ol/coordinate'; import TrackStyle from './track-style' import axios from "axios"; export default defineComponent({ name: 'App', components: {}, setup() { const store = useStore() const that = (getCurrentInstance() as ComponentInternalInstance).appContext.config.globalProperties const SourceMap = new Map(window.cusConfig.trackSource) const state = reactive({ map: <any>null, mapFunc: null, trackPointList: [], trackList: <any>[], formTrackPointStartCount: 0, formTrackPointEndCount: 0, formTrackPointList: [], initTrackPointStartCount: 0, initTrackPointEndCount: 0, initTrackPointList: [], }); const mapLoad = (map: null, func: null) => { state.map = map state.mapFunc = func } const startDraw = (cb: { (evt: any): void; (arg0: { feature: { getGeometry: () => any; }; }): void; }) => { let measureTooltipElement: HTMLDivElement | null; let helpTooltipElement: HTMLDivElement | null; const realLayer = state.map.getLayers().getArray().filter((v: { get: (arg0: string) => string; }) => v.get('layerName') === 'measureLayer') let sketch: { getGeometry: () => { (): any; new(): any; on: { (arg0: string, arg1: (evt: any) => void): any; new(): any; }; }; } | null; let helpTooltip: ol.Overlay; let measureTooltip: ol.Overlay; let continueMsg = '双击结束标绘'; const createMeasureTooltip = () => { const id = 'measureTooltipElementId' if (measureTooltipElement) { state.map.removeOverlay(state.map.getOverlayById(id)) measureTooltipElement.parentNode?.removeChild(measureTooltipElement); } measureTooltipElement = document.createElement('div'); measureTooltipElement.className = 'tooltip tooltip-measure'; measureTooltip = new ol.Overlay({ id, element: measureTooltipElement, offset: [0, -15], positioning: 'bottom-center' }); state.map.addOverlay(measureTooltip); } const createHelpTooltip = () => { const id = 'helpTooltipElementId' if (helpTooltipElement) { state.map.removeOverlay(state.map.getOverlayById(id)) helpTooltipElement.parentNode?.removeChild(helpTooltipElement); } helpTooltipElement = document.createElement('div'); helpTooltipElement.className = 'tooltip hidden'; helpTooltip = new ol.Overlay({ id, element: helpTooltipElement, offset: [15, 0], positioning: 'center-left' }); state.map.addOverlay(helpTooltip); } const formatLength = (line: Geometry) => { // 获取投影坐标系 const sourceProj = state.map.getView().getProjection(); // ol/sphere里有getLength()和getArea()用来测量距离和区域面积,默认的投影坐标系是EPSG:3857, 其中有个options的参数,可以设置投影坐标系 const length = sphere.getLength(line, {projection: sourceProj}); // const length = getLength(line); let output; if (length > 100) { const km = Math.round((length / 1000) * 100) / 100; output = `${km} 千米 <br>${parseFloat(String(km * 0.53995)).toFixed(2)} 海里`; } else { output = `${Math.round(length * 100) / 100} m`; } return output; }; const addInteraction = () => { const id = 'drawName' const draw = new interaction.Draw({ type: 'LineString', //几何图形类型 style: new style.Style({ fill: new style.Fill({ color: "rgba(255, 255, 255, 0.2)", }), stroke: new style.Stroke({ color: "#f3584a", width: 2, }), image: new style.Circle({ radius: 5, stroke: new style.Stroke({ color: "rgba(0, 0, 0, 0.7)", }), fill: new style.Fill({ color: "rgba(255, 255, 255, 0.2)", }), }), }), }); draw.set(id, id) createMeasureTooltip(); //创建测量工具提示框 createHelpTooltip(); //创建帮助提示框 state.map.addInteraction(draw); let listener: EventsKey | EventsKey[]; //绑定交互绘制工具开始绘制的事件 const drawstartHandle = (evt: { feature: { getGeometry: () => { (): any; new(): any; on: { (arg0: string, arg1: (evt: any) => void): any; new(): any; }; }; } | null; coordinate: any; }) => { sketch = evt.feature; //绘制的要素 let tooltipCoord = evt.coordinate;// 绘制的坐标 //绑定change事件,根据绘制几何类型得到测量长度值或面积值,并将其设置到测量工具提示框中显示 listener = sketch?.getGeometry().on('change', function (evt) { const geom = evt.target let output; output = formatLength(geom);//长度值 tooltipCoord = geom.getLastCoordinate();//坐标 if (measureTooltipElement) measureTooltipElement.innerHTML = output;//将测量值设置到测量工具提示框中显示 measureTooltip.setPosition(tooltipCoord);//设置测量工具提示框的显示位置 }); } draw.on('drawstart', drawstartHandle); //绑定交互绘制工具结束绘制的事件 const copy = (value: string) => { const str = document.createElement('input') str.setAttribute('value', value) document.body.appendChild(str) str.select() document.execCommand('copy') document.body.removeChild(str) } const drawendHandle = (evt: { feature: { getGeometry: () => any; }; }) => { state.map.removeInteraction(state.map.getInteractions().getArray().filter((v: { get: (arg0: string) => string; }) => v.get(id) === id)[0]); sketch = null; //置空当前绘制的要素对象 measureTooltipElement?.parentNode?.removeChild(measureTooltipElement); measureTooltipElement = null; //置空测量工具提示框对象 helpTooltipElement?.parentNode?.removeChild(helpTooltipElement); helpTooltipElement = null; //置空测量工具提示框对象 unByKey(listener); draw.un('drawstart', drawstartHandle); draw.un('drawend', drawendHandle); state.map.removeInteraction(state.map.getInteractions().getArray().filter((v: { get: (arg0: string) => string; }) => v.get(id) === id)[0]); state.map.un('pointermove', pointerMoveHandler) cb(evt) } draw.on('drawend', drawendHandle); } addInteraction(); //调用加载绘制交互控件方法,添加绘图进行测量 const pointerMoveHandler = (evt: { dragging: any; coordinate: Coordinate | undefined; }) => { if (evt.dragging) { return; } let helpMsg = '单击开始标绘';//当前默认提示信息 //判断绘制几何类型设置相应的帮助提示信息 if (sketch) { const geom = sketch.getGeometry() helpMsg = continueMsg; // if (geom.getType() === 'Polygon') { // helpMsg = continueMsg; //绘制多边形时提示相应内容 // } else if (geom.getType() === 'LineString') { // helpMsg = continueMsg; //绘制线时提示相应内容 // } } if (helpTooltipElement)helpTooltipElement.innerHTML = helpMsg; //将提示信息设置到对话框中显示 helpTooltip.setPosition(evt.coordinate);//设置帮助提示框的位置 helpTooltipElement?.classList.remove('hidden');//移除帮助提示框的隐藏样式进行显示 }; state.map.on('pointermove', pointerMoveHandler); //地图容器绑定鼠标移动事件,动态显示帮助提示框内容 //地图绑定鼠标移出事件,鼠标移出时为帮助提示框设置隐藏样式 // state.map.getViewport().on('mouseout', () => { // helpTooltipElement?.addClass('hidden'); // }); } const drawTrack = (trackSource: string) => { startDraw((evt) => { const geom = evt.feature.getGeometry() const pMap = new Map() state.trackPointList = geom.getCoordinates().map((v: any) => { const obj = { source: trackSource, position: v, speed: 0 } pMap.set(`${v[0]}-${v[1]}`, obj) return obj }) that.$easyMap.initShape({ map: state.map, layerName: "form-track-point-line", layerZIndex: 9, list: [ { easyMapParams: { id: new Date().getTime(), position: that.$easyMap.formatPosition.wptTwl(state.trackPointList.map(v => that.$easyMap.formatPosition.cptTwpt(v.position))), normalStyle: (f: any, r: any) => TrackStyle.trackLineStyle(f, r, state.map, SourceMap.get(trackSource)?.color, pMap, (s, p) => { state.formTrackPointStartCount++ setTimeout(() => { state.formTrackPointEndCount++ state.formTrackPointList.push(...p) if (state.formTrackPointStartCount === state.formTrackPointEndCount) { that.$easyMap.initShape({ map: state.map, layerName: 'form-track-point', layerZIndex: 10, list: state.formTrackPointList.map((v, i) => { return { easyMapParams: { id: `form-track-point-${i}`, position: that.$easyMap.formatPosition.cptTwpt(v.position), normalStyle: TrackStyle.trackPointStyle(SourceMap.get(v.source).color, v.speed) } } }) }) state.formTrackPointStartCount = 0 state.formTrackPointEndCount = 0 state.formTrackPointList = [] } }, 10) return s }), } } ] }); }) } const onPointFocus = (p1: any, p2: any, p3: any) => { that.$easyMap.getShapeView(state.map, [p1?.position, p2.position, p3?.position].filter(v => v)) const radius = 25 const longRadius = radius * Math.SQRT2 that.$easyMap.initShape({ map: state.map, layerName: "focus", layerZIndex: 20, list: [ { easyMapParams: { id: 'focus', position: that.$easyMap.formatPosition.cptTwpt(p2.position), normalStyle: [new style.Style({ //图层样式 image: new style.RegularShape({ stroke: new style.Stroke({ color: '#9F2EFF', width: 2, lineDash: [ (longRadius * 3) / 10, (longRadius * 4) / 10, (longRadius * 3) / 10, 0 ] }), radius1: radius, rotation: Math.PI / (180 / 45), points: 4 }) })] } } ] }); } const onSubmit = () => { const obj = { type: state.trackPointList[0].source, lines: state.trackPointList.map(v => { return { lon: v.position[0], lat: v.position[1], speed: v.speed } }) } const result = JSON.parse(JSON.stringify(obj)) state.trackList.push(Object.assign(result, {show: false, ID: new Date().getTime()})) state.trackPointList = [] that.$easyMap.initShape({ map: state.map, layerName: "form-track-point-line", layerZIndex: 9, list: [] }); that.$easyMap.initShape({ map: state.map, layerName: "form-track-point", layerZIndex: 10, list: [] }); console.log(result) axios.post("/init-speed-track-api/hujie-track-server/mock", [result], { contentType: "application/json" }).then(res => { console.log(res) }) } const trackShowListCom = computed(() => { return state.trackList.filter((v: { show: any; }) => v.show) }) const initTrack = () => { that.$easyMap.initShape({ map: state.map, layerName: "track-point-line", layerZIndex: 7, list: [] }); that.$easyMap.initShape({ map: state.map, layerName: "track-point", layerZIndex: 8, list: [] }); that.$easyMap.initShape({ map: state.map, layerName: "track-point-line", layerZIndex: 7, list: trackShowListCom.value.map((v: any) => { const pMap = new Map() v.lines.forEach((p: { speed: any; type: any; lon: any; lat: any; }) => { const obj = { source: v.type, position: [p.lon, p.lat], speed: p.speed } pMap.set(`${p.lon}-${p.lat}`, obj) }) return { easyMapParams: { id: v.ID, position: that.$easyMap.formatPosition.wptTwl(v.lines.map((c: { lon: any; lat: any; }) => that.$easyMap.formatPosition.cptTwpt([c.lon, c.lat]))), normalStyle: (f: any, r: any) => TrackStyle.trackLineStyle(f, r, state.map, SourceMap.get(v.type)?.color, pMap, (s, p) => { state.initTrackPointStartCount++ setTimeout(() => { state.initTrackPointEndCount++ state.initTrackPointList.push(...p) if (state.initTrackPointStartCount === state.initTrackPointEndCount) { that.$easyMap.initShape({ map: state.map, layerName: 'track-point', layerZIndex: 8, list: state.initTrackPointList.map((v, i) => { return { easyMapParams: { id: `init-track-point-${v.ID}-${i}`, position: that.$easyMap.formatPosition.cptTwpt(v.position), normalStyle: TrackStyle.trackPointStyle(SourceMap.get(v.source).color, v.speed) } } }) }) state.initTrackPointStartCount = 0 state.initTrackPointEndCount = 0 state.initTrackPointList = [] } }, 10) return s }), } } }) }); } const handleShow = (show: any, item: any) => { item.show = show initTrack() } return { ...toRefs(state), mapLoad, drawTrack, onPointFocus, SourceMap, onSubmit, handleShow } } }) </script> <style lang="scss" scoped> .init-speed-track { width: 100%; height: 100%; position: relative; .map { width: 100%; height: 100vh; } .track { position: absolute; z-index: 20; top: 0; left: 0; .track-line { overflow-y: auto; max-height: 180px; .line { height: 20px; display: flex; align-items: center; .label { width: 90px; } } } .track-point { overflow-y: auto; max-height: 600px; .point { display: flex; .position { font-size: 12px; } border-bottom: 1px solid black; } } } } </style>