소스 검색

轨迹绘制方式

CzRger 3 달 전
부모
커밋
fe5d9b819f

+ 269 - 0
src/components/easyMap/class/ShipTrack.ts

@@ -0,0 +1,269 @@
+import * as format from "ol/format";
+import * as source from "ol/source";
+import * as layer from "ol/layer";
+import * as style from "ol/style";
+import * as extent from "ol/extent";
+import * as geom from "ol/geom";
+import * as ol from "ol";
+//  @ts-ignore
+import DirectionImg from '../images/fangxiang.png'
+import {v4} from "uuid";
+class ShipTrack {
+  private uuid = v4()
+  private infos: any = {}
+  private map: any = null
+  private historyPoints: any = []
+  private realPoints: any = []
+  private color = ''
+  private trackLayer: any = null
+  private pointLayer: any = null
+  private directionLayer: any = null
+  private historyPointCoordinates: any = []
+  private realPointCoordinates: any = []
+  private pointMap: any = new Map()
+  private keyMapper: any = {}
+  private functions = {
+    zoom: () => {}
+  }
+  private zIndex
+  constructor({map, keys = {
+    lon: 'targetLongitude',
+    lat: 'targetLatitude',
+  }, color = '', infos = {}}: any, zIndex = 400) {
+    this.map = map
+    this.color = color
+    this.infos = infos
+    this.keyMapper = keys
+    this.zIndex = zIndex
+  }
+  init(historyPoints: any = [], realPoints: any = []) {
+    if (!this.color) {
+      this.color = randomColor(1)
+    }
+    historyPoints.forEach((v, i) => {
+      this.pointMap.set(`${v[this.keyMapper.lon]}_${v[this.keyMapper.lat]}`, v)
+      this.historyPointCoordinates.push([v[this.keyMapper.lon], v[this.keyMapper.lat]])
+      this.historyPoints.push(v)
+    });
+    realPoints.forEach((v, i) => {
+      this.pointMap.set(`${v[this.keyMapper.lon]}_${v[this.keyMapper.lat]}`, v)
+      this.realPointCoordinates.push([v[this.keyMapper.lon], v[this.keyMapper.lat]])
+      this.realPoints.push(v)
+    });
+    const lineF: any = new ol.Feature({
+      geometry: new geom.LineString([...this.historyPointCoordinates, ...this.realPointCoordinates]),
+    })
+    this.trackLayer = new layer.VectorImage({
+      source: new source.Vector({
+        features: [lineF],
+        wrapX: false,
+      }),
+      zIndex: this.zIndex,
+    });
+    this.trackLayer.set('layerName', this.uuid + '_track')
+    this.map.addLayer(this.trackLayer);
+    this.refreshStyle()
+    this.functions.zoom = debounce(e => {
+      this.refreshStyle()
+    }, 300)
+    this.map.getView().on('change:resolution', this.functions.zoom)
+    return this
+  }
+  focus() {
+    getShapeView(this.map, this.trackLayer.getSource().getFeatures()[0].getGeometry().getCoordinates())
+  }
+  show() {
+    this.trackLayer.setVisible(true)
+    this.pointLayer.setVisible(true)
+    this.directionLayer.setVisible(true)
+  }
+  hide() {
+    this.trackLayer.setVisible(false)
+    this.pointLayer.setVisible(false)
+    this.directionLayer.setVisible(false)
+  }
+  refreshStyle() {
+    this.map.getLayers().getArray().filter(v => [this.uuid + '_direction', this.uuid + '_point'].includes(v.get('layerName'))).forEach(v => {
+      this.map.removeLayer(v)
+    })
+    const resolution = this.map.getView().getResolution()
+    let radio = (30 * resolution);
+    if (this.map.getView().getZoom() == this.map.getView().getMaxZoom()) {
+      radio = 0
+    }
+    const simplifyGeom: any = new geom.LineString([...this.historyPointCoordinates, ...this.realPointCoordinates]).simplify(radio)
+    const simplifyCoor = simplifyGeom.getCoordinates()
+    const trackFeat = this.trackLayer.getSource().getFeatures()[0]
+    trackFeat.setGeometry(simplifyGeom)
+    trackFeat.setStyle(new style.Style({
+      stroke: new style.Stroke({
+        color: this.color,
+        width: 2,
+      }),
+    }))
+    const directionFeatures: any = []
+    const pointFeatures: any = []
+    simplifyCoor.forEach((v, i) => {
+      if (i > 0) {
+        const last = simplifyCoor[i - 1]
+        const dx = v[0] - last[0];
+        const dy = v[1] - last[1];
+        const rotation = Math.atan2(dy, dx) * -1;
+        const directFeat = new format.WKT().readFeature(`POINT(${(v[0] + last[0]) / 2} ${(v[1] + last[1]) / 2})`)
+        directFeat.set('rotation_', rotation)
+        directionFeatures.push(directFeat)
+      }
+      const d = this.pointMap.get(`${v[0]}_${v[1]}`)
+      const feat: any = new format.WKT().readFeature(`POINT(${v[0]} ${v[1]})`)
+      feat.set('data_', d)
+      feat.set('type_', 'track-point')
+      pointFeatures.push(feat)
+    });
+    this.pointLayer = new layer.WebGLPoints({
+      zIndex: this.zIndex + 2,
+      source: new source.Vector({
+        features: pointFeatures
+      }) as any,
+      style: {
+        'circle-radius': ['match', ['get', 'hover_'], 1, 6, 4],
+        'circle-fill-color': this.color,
+        'circle-stroke-color': '#fff',
+        'circle-stroke-width': 1,
+      },
+    })
+    this.pointLayer.set('layerName', this.uuid + '_point')
+    this.map.addLayer(this.pointLayer)
+    this.directionLayer = new layer.WebGLPoints({
+      zIndex: this.zIndex + 1,
+      source: new source.Vector({
+        features: directionFeatures
+      }) as any,
+      style: {
+        'icon-src': DirectionImg,
+        'icon-color': this.color,
+        'icon-rotation': ['get', 'rotation_'],
+        'icon-width': [
+          'interpolate',
+          ['exponential', 2],
+          ['zoom'],
+          0, 26,
+          20, 16
+        ],
+        'icon-height': [
+          'interpolate',
+          ['exponential', 2],
+          ['zoom'],
+          0, 26,
+          20, 22
+        ],
+      },
+    })
+    this.directionLayer.set('layerName', this.uuid + '_direction')
+    this.map.addLayer(this.directionLayer)
+  }
+  add(newPoints) {
+    newPoints.forEach((v, i) => {
+      this.pointMap.set(`${v[this.keyMapper.lon]}_${v[this.keyMapper.lat]}`, v)
+      this.historyPointCoordinates.push([v[this.keyMapper.lon], v[this.keyMapper.lat]])
+      this.historyPoints.push(v)
+    })
+    this.refreshStyle()
+  }
+  update(newPoints) {
+    newPoints.forEach((v, i) => {
+      this.pointMap.set(`${v[this.keyMapper.lon]}_${v[this.keyMapper.lat]}`, v)
+      this.realPointCoordinates.push([v[this.keyMapper.lon], v[this.keyMapper.lat]])
+      this.realPoints.push(v)
+    })
+    this.refreshStyle()
+  }
+  setColor(val) {
+    this.color = val
+    this.refreshStyle()
+  }
+  destroy() {
+    this.map.getView().un('change:resolution', this.functions.zoom)
+    this.map.getLayers().getArray().filter(v => [this.uuid + '_direction', this.uuid + '_point', this.uuid + '_track'].includes(v.get('layerName'))).forEach(v => {
+      this.map.removeLayer(v)
+    })
+  }
+}
+const randomColor = (opacity) => `rgba(${randomNum(0, 255)}, ${randomNum(0, 255)}, ${randomNum(0, 255)}, ${opacity ? opacity : randomNum(0.5, 1, 1)})`
+const randomNum = (min = 0, max = 0, decimal= 0) => {
+  // 获取数值的小数部分
+  const getDecimalNum = (data: number) => {
+    return Number(data.toString().split('.')[1]);
+  }
+  let min_z = Math.trunc(min); // 最小值的整数部分
+  let max_z = Math.trunc(max); // 最大值的整数部分
+  // 判断是否存在小数部分,不存在的话为0
+  let min_x = isNaN(getDecimalNum(min)) ? 0 : getDecimalNum(min);  // 最小值的小数部分
+  let max_x = isNaN(getDecimalNum(max)) ? 0 : getDecimalNum(max);  // 最大值的小数部分
+
+  // 区分有小数和没小数的情况
+  if (min_x > 0 || max_x > 0 || decimal > 0) {
+    // 整数部分随机数
+    let z = parseInt(String(Math.random() * (max_z - min_z + 1) + min_z), 10);
+    // 小数部分随机数
+    let x = 0;
+    // 小数部分随机数最大位数
+    let max_decimal = min_x.toString().length > max_x.toString().length ? min_x.toString().length : max_x.toString().length;
+    max_decimal = decimal > max_decimal ? decimal : max_decimal;
+    // 判断随机出的整数部分,是否等于最小值或者最大值
+    if(z == min_z || z == max_z){
+      if(z == min_z){
+        // 整数部分随机数等于最小值,那么应该从最小值的小数部分开始,到小数位数的最大值随机就可以
+        x = parseInt(String(Math.random() * (Math.pow(10, max_decimal) - min_x) + min_x), 10);
+      }else{
+        // 整数部分随机数等于最大值,那么应该从0开始,到最大值小数部分
+        x = parseInt(String(Math.random() * (max_x + 1)), 10);
+      }
+    }else{
+      // 整数部分在最大最小值区间的,就从0到小数位数的最大值随机就可以
+      x = parseInt(String(Math.random() * (Math.pow(10, max_decimal))), 10);
+    }
+    return Number(`${z}.${x}`);
+  } else {
+    return parseInt(String(Math.random() * (max_z - min_z + 1) + min_z), 10);
+  }
+}
+const getShapeView = (map, position, L = 600, defaultZoom = 12) => {
+  const center = extent.getCenter(extent.boundingExtent(position))
+  let x = 0
+  let y = 0
+  position.forEach(v => {
+    if (Math.abs(v[0] - center[0]) > x) {
+      x = Math.abs(v[0] - center[0])
+    }
+    if (Math.abs(v[1] - center[1]) > y) {
+      y = Math.abs(v[1] - center[1])
+    }
+  })
+  const resolution = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)) / (L / document.body.clientWidth * document.body.clientHeight)
+  if (map) {
+    if (position.length > 1) {
+      map.getView().animate({
+        center, resolution
+      })
+    } else {
+      map.getView().animate({
+        center, zoom: defaultZoom
+      })
+    }
+  }
+  return {
+    center, resolution
+  }
+}
+const debounce = function (cb: any, ms = 0) {
+  let timer: any = null
+  return function () {
+    if (timer) clearTimeout(timer)
+    timer = setTimeout(() => {
+      //  @ts-ignore
+      cb.apply(this, arguments)
+      timer = null
+    }, ms)
+  }
+}
+export default ShipTrack

BIN
src/components/easyMap/images/fangxiang.png


+ 35 - 91
src/stores/ship-map/ship-map.ts

@@ -8,9 +8,9 @@ import MapStyle from "./map-style";
 import * as ol from "ol";
 import {formatGetParam, randomColor, YMDHms} from "@/utils/util";
 import {formatPosition, getShapeView} from "@/utils/easyMap";
-import { v4 } from "uuid";
 import {shipArchiveDetail} from "@/api/modules/web/archive";
 import axios from "axios";
+import ShipTrack from '@/components/easyMap/class/ShipTrack'
 
 export const useShipMapStore = defineStore('shipMap', () => {
   const state: any = reactive({
@@ -41,7 +41,8 @@ export const useShipMapStore = defineStore('shipMap', () => {
       targetNameEn: 'targetNameEn',
       targetSourceJson: 'targetSource',
       shipId: 'shipArchiveId',
-    }
+    },
+    selected: null
   })
   const initMap = (map, mapFunc, {trackPointDom}) => {
     state.map = map
@@ -69,14 +70,28 @@ export const useShipMapStore = defineStore('shipMap', () => {
     initWarningWS()
   }
   const mapPointerMove = (ev) => {
+    if (ev.dragging) {
+      return;
+    }
     let pixel = ev.pixel
+    if (state.selected !== null) {
+      state.selected.set('hover_', 0);
+      state.selected = null;
+    }
     let feature = state.map.forEachFeatureAtPixel(pixel, function (feature) {
       return feature
     })
-    if (feature && (feature.get('_featureType') == 'ship' || feature.get('_featureType') == 'trackPoint')) {
-      state.map.getTargetElement().style.cursor = 'pointer'
-      state.trackHoverData = feature.get('_data')
-      state.ws.overlayTrack.setPosition(feature.getGeometry().getCoordinates())
+    if (feature) {
+      if (feature.get('_featureType') == 'ship') {
+        state.map.getTargetElement().style.cursor = 'pointer'
+        state.trackHoverData = feature.get('_data')
+        state.ws.overlayTrack.setPosition(feature.getGeometry().getCoordinates())
+      } else if (feature.get('type_') == 'track-point') {
+        state.selected = feature;
+        state.map.getTargetElement().style.cursor = 'pointer'
+        state.trackHoverData = feature.get('data_')
+        state.ws.overlayTrack.setPosition(feature.getGeometry().getCoordinates())
+      }
     } else {
       state.map.getTargetElement().style.cursor = ''
       state.trackHoverData = null
@@ -95,32 +110,18 @@ export const useShipMapStore = defineStore('shipMap', () => {
       if (!state.trackMap.has(feature.get('_id'))) {
         const isWarning = feature.get('_warning')
         const d = feature.get('_data')
+        const color = isWarning ? '#ea3147' : '#FC60FF'
         state.trackMap.set(feature.get('_id'), {
+          shipTrackClass: null,
           isWarning: !!isWarning,
           data: feature.get('_data'),
-          color: isWarning ? '#ea3147' : '#FC60FF', //randomColor(1),
-          history: [],
-          real: isWarning ? [] : [d],
+          color: color, //randomColor(1),
           trackId: feature.get('_trackId'),
-          moveToTrack: () => {
-            const position: any = []
-            const t = state.trackMap.get(feature.get('_id'))
-            const arr = [...t.history, ...t.real]
-            arr.forEach(v => {
-              position.push([v[state.trackKeys.lon], v[state.trackKeys.lat]])
-            })
-            if (position.length > 0) {
-              getShapeView(state.map, position)
-            }
-          },
           // 直接用图层的话declutter: true会报错,Uncaught TypeError: Failed to execute 'clip' on 'CanvasRenderingContext2D': parameter 1 is not of type 'Path2D'.
-          lineLayer: 'lineLayer_' + feature.get('_id'),
-          pointsLayer: 'pointsLayer_' + feature.get('_id'),
           warningLayer: 'warningLayer_' + feature.get('_id'),
           del: () => {
-            state.map.removeLayer(state.map.getLayers().getArray().filter(v => v.get('__layerName') === state.trackMap.get(feature.get('_id')).lineLayer)[0])
-            state.map.removeLayer(state.map.getLayers().getArray().filter(v => v.get('__layerName') === state.trackMap.get(feature.get('_id')).pointsLayer)[0])
             state.map.removeLayer(state.map.getLayers().getArray().filter(v => v.get('__layerName') === state.trackMap.get(feature.get('_id')).warningLayer)[0])
+            state.trackMap.get(feature.get('_id')).shipTrackClass.destroy()
             state.trackMap.delete(feature.get('_id'))
             if (realTrackMapCpt.value.size === 0) {
               state.ws.instance?.close()
@@ -134,59 +135,13 @@ export const useShipMapStore = defineStore('shipMap', () => {
           },
           visibleTrack: (visible) => {
             const t = state.trackMap.get(feature.get('_id'))
-            state.map.getLayers().getArray().filter(v => v.get('__layerName') === t.lineLayer)[0]?.setVisible(visible)
-            state.map.getLayers().getArray().filter(v => v.get('__layerName') === t.pointsLayer)[0]?.setVisible(visible)
             state.map.getLayers().getArray().filter(v => v.get('__layerName') === t.warningLayer)[0]?.setVisible(visible)
             t.showTrack = visible
             if (visible) {
-              t.moveToTrack()
-            }
-          },
-          refreshTrackStyle: () => {
-            const t = state.trackMap.get(feature.get('_id'))
-            const arr = [...t.history, ...t.real]
-            let lineWkt = ''
-            if (arr.length > 1) {
-              arr.forEach((v, i) => {
-                if (i === 0) {
-                  lineWkt += `LINESTRING(${v[state.trackKeys.lon]} ${v[state.trackKeys.lat]}`
-                } else if (i === arr.length - 1) {
-                  lineWkt += `,${v[state.trackKeys.lon]} ${v[state.trackKeys.lat]})`
-                } else {
-                  lineWkt += `,${v[state.trackKeys.lon]} ${v[state.trackKeys.lat]}`
-                }
-                v.wkt = `POINT(${v[state.trackKeys.lon]} ${v[state.trackKeys.lat]})`
-              })
-            }
-            if (lineWkt) {
-              const lS = new source.Vector({
-                features: [],
-                wrapX: false
-              })
-              const lineF: any = new format.WKT().readFeature(lineWkt)
-              lineF.set('trackPointList', arr)
-              lineF.setStyle((f, r) => MapStyle.trackStyle(f, r, state.map, t.color, (_s, pointList) => {
-                const pointFeatures: any = []
-                pointList.forEach(DATA => {
-                  try {
-                    const feat: any = new format.WKT().readFeature(DATA.wkt)
-                    feat.set('_featureType', 'trackPoint')
-                    feat.set('_data', DATA)
-                    feat.setStyle(MapStyle.trackPointNormalStyle(t.color))
-                    pointFeatures.push(feat)
-                  } catch (e) {
-                    console.log(e)
-                  }
-                })
-                lS.clear()
-                lS.addFeatures(pointFeatures)
-                return _s
-              }))
-              state.map.getLayers().getArray().filter(v => v.get('__layerName') === t.lineLayer)[0].setSource(new source.Vector({
-                features: [lineF],
-                wrapX: false
-              }))
-              state.map.getLayers().getArray().filter(v => v.get('__layerName') === t.pointsLayer)[0].setSource(lS)
+              t.shipTrackClass.show()
+              t.shipTrackClass.focus()
+            } else {
+              t.shipTrackClass.hide()
             }
           },
           showTrack: true,
@@ -211,21 +166,10 @@ export const useShipMapStore = defineStore('shipMap', () => {
             }
           })
         }
-        const lineLayer = new layer.Vector({
-          zIndex: 4000,
-          __layerName: state.trackMap.get(feature.get('_id')).lineLayer
-        })
-        const pointsLayer = new layer.Vector({
-          zIndex: 4100,
-          declutter: true,
-          __layerName: state.trackMap.get(feature.get('_id')).pointsLayer
-        })
         const warningLayer = new layer.Vector({
           zIndex: 3999,
-          __layerName: state.trackMap.get(feature.get('_id')).warningLayer,
         })
-        state.map.addLayer(lineLayer)
-        state.map.addLayer(pointsLayer)
+        warningLayer.set('__layerName', state.trackMap.get(feature.get('_id')).warningLayer)
         state.map.addLayer(warningLayer)
         if (isWarning) {
           const wf: any = new format.WKT().readFeature(`POINT(${d[state.trackKeys.lon]} ${d[state.trackKeys.lat]})`)
@@ -235,6 +179,8 @@ export const useShipMapStore = defineStore('shipMap', () => {
             wrapX: false,
           }))
         }
+        const shipTrack = new ShipTrack({map: state.map, color: color}).init([], [d])
+        state.trackMap.get(feature.get('_id')).shipTrackClass = shipTrack
         const ws = new WebSocket(`ws://${location.host}/history-track-ws-api/historyshiptrack`)
         ws.onopen = (e) => {
           if (isWarning) {
@@ -259,10 +205,9 @@ export const useShipMapStore = defineStore('shipMap', () => {
             if (json.length === 0) {
               ws.close()
             } else if (json?.length > 0) {
-              state.trackMap.get(feature.get('_id')).history.push(...json)
-              state.trackMap.get(feature.get('_id')).refreshTrackStyle()
+              state.trackMap.get(feature.get('_id')).shipTrackClass.add(json)
               if (isWarning) {
-                state.trackMap.get(feature.get('_id')).moveToTrack()
+                state.trackMap.get(feature.get('_id')).shipTrackClass.focus()
               }
             }
           } catch (e) {
@@ -410,10 +355,9 @@ export const useShipMapStore = defineStore('shipMap', () => {
         // 实时轨迹
         const t = state.trackMap.get(feat.get('_id'))
         if (t && t.trackId !== feat.get('_trackId')) {
-          t.real.push(feat.get('_data'))
           t.trackId = feat.get('_trackId')
           t.data = feat.get('_data')
-          t.refreshTrackStyle()
+          t.shipTrackClass.update([feat.get('_data')])
         }
         return feat
       } catch (e) {

+ 27 - 12
src/views/web/track/index.vue

@@ -17,7 +17,7 @@
         <template v-for="([key, value], index) in ShipMapStore.trackMap">
           <div class="row" :style="`color: ${value.color};`">
             <div class="index">{{index + 1}}</div>
-            <div class="target __hover" @click="value.moveToTrack(), value.showArchive = true">{{value.isWarning ? value.data[ShipMapStore.trackKeys.mergeTarget] : key}}</div>
+            <div class="target __hover" @click="value.shipTrackClass.focus(), value.showArchive = true">{{value.isWarning ? value.data[ShipMapStore.trackKeys.mergeTarget] : key}}</div>
             <div class="time">
               {{getDuration(value)}}
             </div>
@@ -28,9 +28,9 @@
               <el-tooltip :enterable="false" placement="top" content="显示轨迹" v-else>
                 <SvgIcon class="__hover" name="eye-close" size="16" color="#22E622" @click="value.visibleTrack(true)"/>
               </el-tooltip>
-<!--              <el-tooltip :enterable="false" placement="top" content="轨迹分析" v-if="value.history?.length > 0">-->
-<!--                <SvgIcon class="__hover" name="panel" size="16" color="#48DCFD"/>-->
-<!--              </el-tooltip>-->
+              <el-tooltip :enterable="false" placement="top" content="轨迹分析" v-if="[...value.shipTrackClass.historyPoints, ...value.shipTrackClass.realPoints].length > 0">
+                <SvgIcon class="__hover" name="panel" size="16" color="#48DCFD" @click="onAnalysis(value)"/>
+              </el-tooltip>
               <el-tooltip :enterable="false" placement="top" content="调色盘">
                 <div class="color">
                   <SvgIcon class="__hover" name="color" size="16" color="#FF7223"/>
@@ -46,6 +46,7 @@
       </div>
     </div>
   </DragWindow>
+  <AnalysisCom v-model:show="state.showAnalysis" ref="ref_analysis" :mapFunc="mapFunc" :map="map"/>
 </template>
 
 <script setup lang="ts">
@@ -53,12 +54,14 @@ import {computed, getCurrentInstance, markRaw, nextTick, onMounted, reactive, re
 import DragWindow from '../components/drag-window.vue'
 import {useShipMapStore} from "@/stores";
 import {comTimeByArea} from "@/utils/util";
+import AnalysisCom from "@/views/web/track/analysis.vue";
 
 const ShipMapStore = useShipMapStore()
 const {proxy} = getCurrentInstance()
 const props = defineProps({
   show: {},
-  mapFunc: {}
+  mapFunc: {},
+  map: {}
 })
 const state: any = reactive({
   layout: {
@@ -66,27 +69,39 @@ const state: any = reactive({
     left: 85,
     top: 110
   },
+  showAnalysis: false
 })
+const ref_analysis = ref()
 const getDuration = (value) => {
   let start = null
   let end = null
-  if (value.history.length > 0) {
-    start = value.history[0].mergeTime
-    end = value.history[value.history.length - 1].mergeTime
+  const stc = value.shipTrackClass
+  if (stc.historyPoints.length > 0) {
+    start = stc.historyPoints[0].mergeTime
+    end = stc.historyPoints[stc.historyPoints.length - 1].mergeTime
   }
-  if (value.real.length > 0) {
+  if (stc.realPoints.length > 0) {
     if (!start) {
-      start = value.real[0].mergeTime
+      start = stc.realPoints[0].mergeTime
     }
-    end = value.real[value.real.length - 1].mergeTime
+    end = stc.realPoints[stc.realPoints.length - 1].mergeTime
   }
   return comTimeByArea(start, end, true)
 }
 const handleColor = (value) => {
   nextTick(() => {
-    value.refreshTrackStyle?.()
+    value.shipTrackClass.setColor(value.color)
+    value.shipTrackClass.refreshStyle()
   })
 }
+const onAnalysis = (value) => {
+  ref_analysis.value.init({
+    width: 430,
+    left: state.layout.width + state.layout.left + 20,
+    top: state.layout.top,
+  }, [...value.shipTrackClass.historyPoints, ...value.shipTrackClass.realPoints])
+  state.showAnalysis = true
+}
 </script>
 
 <style lang="scss" scoped>

+ 1 - 1
src/views/web/warning/index.vue

@@ -305,7 +305,7 @@ const toArea = (row) => {
 const toTrack = (ship, warning) => {
   const id = warning.id + '_' + ship[ShipMapStore.trackKeys.mergeTarget]
   if (ShipMapStore.trackMap.has(id)) {
-    ShipMapStore.trackMap.get(id).moveToTrack()
+    ShipMapStore.trackMap.get(id).shipTrackClass.focus()
   } else {
     warnRuleDetail(proxy.$util.formatGetParam({id: warning.ruleId})).then(res => {
       if (res.code == 0) {