import {defineStore} from "pinia"; import {ElMessage} from "element-plus"; import {computed, reactive, toRefs} from "vue"; import * as format from "ol/format"; import * as layer from "ol/layer"; import * as source from "ol/source"; import MapStyle from "./map-style"; import * as ol from "ol"; import {randomColor, YMDHms} from "@/utils/util"; import {formatPosition, getShapeView} from "@/utils/easyMap"; export const useShipMapStore = defineStore('shipMap', () => { const state: any = reactive({ warningOpen: true, map: null, mapFunc: null, zoom: 0, layerWMS: null, ws: { instance: null, layerShip: null, overlayTrack: null, }, trackHoverData: null, trackMap: new Map() }) const initMap = (map, mapFunc, {trackPointDom}) => { state.map = map state.mapFunc = mapFunc state.zoom = state.map.getView().getZoom() state.map.on('movestart', e => { map.un('pointermove', mapPointerMove) map.un('singleclick', mapSingleClick) }) state.map.on('moveend', e => { state.trackHover = new Date().getTime() state.zoom = e.map.getView().getZoom() switchZoom() map.on('singleclick', mapSingleClick) map.on('pointermove', mapPointerMove) }) state.ws.overlayTrack = new ol.Overlay({ element: trackPointDom, autoPan: false, offset: [0, -22], positioning: 'bottom-center', stopEvent: true, }) state.map.addOverlay(state.ws.overlayTrack) } const mapPointerMove = (ev) => { let pixel = ev.pixel 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()) } else { state.map.getTargetElement().style.cursor = '' state.trackHoverData = null state.ws.overlayTrack.setPosition(undefined) } } const mapSingleClick = (ev) => { let pixel = ev.pixel let feature = state.map.forEachFeatureAtPixel(pixel, function (feature) { return feature }) if (feature && feature.get('_featureType') == 'ship') { if (!state.trackMap.has(feature.get('_id'))) { const d = feature.get('_data') state.trackMap.set(feature.get('_id'), { data: feature.get('_data'), color: randomColor(1), history: [], real: [d], trackId: feature.get('_trackId'), moveToTrack: () => { const position = [] const t = state.trackMap.get(feature.get('_id')) const arr = [...t.history, ...t.real] arr.forEach(v => { position.push([v.targetLongitude, v.targetLatitude]) }) 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'), 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.trackMap.delete(feature.get('_id')) }, 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) 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.targetLongitude} ${v.targetLatitude}` } else if (i === arr.length - 1) { lineWkt += `,${v.targetLongitude} ${v.targetLatitude})` } else { lineWkt += `,${v.targetLongitude} ${v.targetLatitude}` } v.wkt = `POINT(${v.targetLongitude} ${v.targetLatitude})` }) } 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) } }, showTrack: true, showArchive: true, archiveLayout: { width: 350, top: 10, left: state.mapFunc.mapWidth - 350 - 10 }, archiveParams: { tab: 1, } }) 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 }) state.map.addLayer(lineLayer) state.map.addLayer(pointsLayer) const ws = new WebSocket(`ws://${location.host}/history-track-ws-api/history-fkShips-track`) ws.onopen = (e) => { const str = { shipId: feature.get('_id'), startTime: YMDHms(new Date(d.mergeTime).getTime() - 1000 * 60 * 60), endTime: YMDHms(d.mergeTime), searchType: 1, sendInterval: 1 } ws.send(JSON.stringify(str)) } ws.onmessage = (e) => { try { const json = JSON.parse(e.data) if (json.message === '查询结束') { ws.close() } else if (json.data?.length > 0) { state.trackMap.get(feature.get('_id')).history.push(...json.data.map(v => ({ targetName: v.shipName, mergeTarget: v.shipId, targetLongitude: v.shipLon, targetLatitude: v.shipLat, targetCourse: v.shipCourse, targetSpeed: v.shipSpeed, mergeTime: v.trackTime, targetSourceJson: v.targetSource, }))) state.trackMap.get(feature.get('_id')).refreshTrackStyle() } } catch (e) { } } initWebSocket() } } } const zoomWMS = computed(() => { return state.zoom <= 14 }) const switchZoom = () => { if (zoomWMS.value) { if (state.ws.instance && state.trackMap.size === 0) { state.ws.instance.close() state.ws.instance = null state.map.removeLayer(state.ws.layerShip) state.ws.layerShip = null } // 瓦片 initWMS() // ws if (state.trackMap.size > 0) { initWebSocket() } } else { if (state.layerWMS) { state.map.removeLayer(state.layerWMS) state.layerWMS = null } // ws initWebSocket() } } const initWMS = () => { const __flag = 'shipWMS' if (!state.layerWMS) { const _l = state.map?.getLayers().getArray().filter(v => v.get('__layerName') === __flag) if (_l?.length > 0) { _l.forEach(v => { state.map.removeLayer(v) }) } const _tileWMS = new source.TileWMS({ url: `/geoserver-api/geoserver/redis/wms`, params: { 'FORMAT': 'image/png8', 'VERSION': '1.1.1', LAYERS: 'redis:geo_fusion_ship', "exceptions": 'application/vnd.ogc.se_inimage', // CQL_FILTER: CQL, refresh: new Date().getTime() } }) state.layerWMS = new layer.Tile({ source: _tileWMS, zIndex: 3333, __layerName: __flag, }) state.map.addLayer(state.layerWMS) } } const getBBOX = () => { return `BBOX(location, ${state.map.getView().calculateExtent(state.map.getSize()).join(',')})`; }; const getWSParams = () => { const param = { cql: '', realCql: '', userId: "18889231165" } let idCql = '' if (state.trackMap.size > 0) { let arr = [] state.trackMap.forEach((v, k) => { arr.push(`'${k}'`) }) idCql = `mergeTarget in (${arr.join(',')})` } if (zoomWMS.value) { param.cql = idCql } else { param.cql = getBBOX() param.realCql = idCql } return JSON.stringify(param) } const initWebSocket = () => { if (!state.ws.instance) { state.ws.instance = new WebSocket(`ws://${location.host}/rh-ws-api/webSocket`) state.ws.instance.onopen = (e) => { state.ws.instance.send(getWSParams()) } state.ws.instance.onmessage = (e) => { try { const json = JSON.parse(e.data) const shipData = json.data initShip(shipData) } catch (e) { } } } else { if (state.ws.instance.readyState == 1) { state.ws.instance.send(getWSParams()) } } } const initShip = (data) => { if (!state.ws.layerShip) { state.ws.layerShip = new layer.Vector({ zIndex: 4444, style: (f) => { return MapStyle.ShipNormalStyle({ course: f.get('_course'), speed: f.get('_speed'), head: f.get('_head'), mergeType: f.get('_mergeType'), color: state.trackMap.get(f.get('_id'))?.color }) } }) state.map.addLayer(state.ws.layerShip) } // 动态拼接数据的唯一标识DATA,不可修改 const features = data.map(v => { try { const feat: any = new format.WKT().readFeature(`POINT(${v.targetLongitude} ${v.targetLatitude})`) feat.set('_course', v.targetCourse || 0) feat.set('_speed', v.targetSpeed || 0) feat.set('_head', v.targetHeading) feat.set('_mergeType', v.mergeType) feat.set('_id', v.mergeTarget) feat.set('_trackId', v.mergeId) feat.set('_data', v) feat.set('_featureType', 'ship') // 实时轨迹 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() } return feat } catch (e) { console.log(e) } }) const vectorSource = new source.Vector({ features: features, wrapX: false }); state.ws.layerShip.setSource(vectorSource) } return { ...toRefs(state), initMap, } })