123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534 |
- <template>
- <div class="init-speed-track" v-loading="loading" element-loading-background="rgba(0, 0, 0, 0.5)">
- <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 in formParams">
- <div class="item">
- <span>{{item.label}}:</span><el-input v-model="form[item.value]" clearable/>
- </div>
- </template>
- <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";
- import {ElMessage} from "element-plus";
- 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 formParams = window.cusConfig.trackParams
- const state = reactive({
- map: <any>null,
- mapFunc: null,
- trackPointList: [],
- trackList: <any>[],
- formTrackPointStartCount: 0,
- formTrackPointEndCount: 0,
- formTrackPointList: [],
- initTrackPointStartCount: 0,
- initTrackPointEndCount: 0,
- initTrackPointList: [],
- loading: false,
- form: {}
- });
- formParams.forEach(v => {
- state.form[v.value] = v.init
- })
- 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
- }
- })
- }
- Object.assign(obj, state.form)
- const result = JSON.parse(JSON.stringify(obj))
- console.log(result)
- state.loading = true
- axios.post("/init-speed-track-api/hujie-track-server/mock", [result], {
- contentType: "application/json"
- }).then(res => {
- console.log(res)
- if (res.status === 200 && res.data) {
- ElMessage.success('添加成功!')
- 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: []
- });
- } else {
- ElMessage.error('添加失败!')
- }
- state.loading = false
- })
- }
- 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,
- formParams
- }
- }
- })
- </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;
- .item {
- display: flex;
- align-items: center;
- white-space: nowrap;
- }
- .point {
- display: flex;
- .position {
- font-size: 12px;
- }
- border-bottom: 1px solid black;
- }
- }
- }
- }
- </style>
|