<template>
<div>
<div :id="mapId" class="map-x" style="width: 100%; height: 300px"></div>
</div>
</template>
<script lang="ts">
import 'ol/ol.css'
import { Feature, Map, View } from 'ol'
import { Tile as TileLayer } from 'ol/layer'
import XYZ from 'ol/source/XYZ'
import { fromLonLat, transform } from 'ol/proj'
import VectorLayer from 'ol/layer/Vector'
import { Vector as VectorSource } from 'ol/source'
import { LineString, Polygon } from 'ol/geom'
import Point from 'ol/geom/Point.js'
import {
Style,
Circle as CircleStyle,
Icon,
Fill,
RegularShape,
Stroke,
Text,
} from 'ol/style'
import {
defineComponent,
nextTick,
onMounted,
ref,
unref,
reactive,
defineExpose,
} from 'vue'
import {
getTrajectoryApi,
getShipBerthInfoApi,
getShipBerthInfoEndApi,
} from '@/api/crewInfo'
export default defineComponent({
props: {
// 地图id
mapId: {
type: String,
default: 'map',
},
},
emits: ["getTimeData"],
setup(props, ctx) {
const state = reactive({
dotsData: [],
map: null,
shipTrackSource: null,
shipTrackLayer: null,
featureMove: null,
carPoints: [], //车还要走的点
routeIndex: 0, //当前小车所在的路段
timer: null,
coordinates: [],
newCoordinates: [],
carPoint: null,
carLayer: null,
idValue: '',
})
onMounted(() => {
state.idValue = props.mapId as string
initMap()
})
const initMap = () => {
state.map = new Map({
target: state.idValue,
view: new View({
center: fromLonLat([121.20256119692851, 27.85807498090228]),
zoom: 14,
}),
layers: [
new TileLayer({
source: new XYZ({
url: 'http://wprd0{1-4}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&style=7&x={x}&y={y}&z={z}',
}),
visible: true,
}),
],
})
state.shipTrackSource = new VectorSource({
wrapX: false,
})
state.shipTrackLayer = new VectorLayer({
source: state.shipTrackSource,
zIndex: 99,
// name: 'shipTrack',
style: function (feature, resolution) {
var featureType = feature.get('featureType')
if (featureType === 'shipTrackLine') {
var styles = []
let line_info = {
color: 'green',
width: 6,
zIndex: 96,
}
styles.push(
new Style({
stroke: new Stroke(line_info),
})
)
// 动态展示箭头 按需引入
// const geometry = feature.getGeometry()
// // 轨迹地理长度
// const totalLength = geometry.getLength()
// // 像素间隔步长
// let step = 100
// // 将像素步长转实际地理距离步长
// let StepLength = step * resolution
// // 箭头总数
// let arrowNum = Math.floor(totalLength / StepLength)
// const rotations = []
// const distances = [0]
// let index = 0
// geometry.forEachSegment(function (start, end) {
// let dx = end[0] - start[0]
// let dy = end[1] - start[1]
// let rotation = Math.atan2(dy, dx)
// distances.unshift(Math.sqrt(dx ** 2 + dy ** 2) + distances[0])
// rotations.push(rotation)
// })
// for (let i = 1; i < arrowNum; i++) {
// let arrow_coor = geometry.getCoordinateAt((i * 1.0) / arrowNum)
// const d = i * StepLength
// const grid = distances.findIndex((x) => x <= d)
// styles.push(
// new Style({
// geometry: new Point(arrow_coor),
// image: new Icon({
// src: require('@/assets/map/arrow.png'),
// opacity: 1,
// anchor: [0.5, 0.5],
// rotateWithView: true,
// rotation: -rotations[distances.length - grid - 1],
// scale: 0.06,
// }),
// zIndex: 4,
// })
// )
// }
return styles
}
},
// declutter: false
})
state.map.addLayer(state.shipTrackLayer)
}
const addPointAndView = async (data) => {
//画轨迹线
await drawLine(data)
//开始动
moveStart()
}
//轨迹线 把每个点连起来
const drawLine = (data) => {
state.shipTrackSource.clear()
let line = new Feature({
geometry: new LineString(data),
featureType: 'shipTrackLine',
})
state.shipTrackSource.addFeature(line)
}
const drawBtmLine = () => {
let line = new Feature({
geometry: new LineString(state.coordinates),
})
let source = new VectorSource({
features: [line],
})
let lineLayer = new VectorLayer({
source: source,
style: new Style({
stroke: new Stroke({
color: '#E0E0E0',
width: 6,
// zIndex: 90,
}),
}),
})
state.map.addLayer(lineLayer)
}
const drawStart = (data: any) => {
let point = new Feature({
geometry: new Point(data),
})
let source = new VectorSource({
features: [point],
})
let lineLayer = new VectorLayer({
source: source,
style: new Style({
image: new Icon({
src: require('@/assets/map/start.png'),
scale: 0.3,
anchor: [0.5, 1],
}),
}),
})
state.map.addLayer(lineLayer)
}
const drawEnd = (data: any, time: any, isShowIcon: Boolean) => {
let point = new Feature({
geometry: new Point(data),
})
if (isShowIcon) {
// 设置小车样式
point.setStyle(
new Style({
image: new Icon({
src: require('@/assets/map/ship.png'),
scale: 1,
anchor: [0.5, 0.5],
// rotation: -state.countRotate(),
}),
})
)
}
let source = new VectorSource({
features: [point],
})
let lineLayer = new VectorLayer({
source: source,
style: new Style({
image: new Icon({
src: require('@/assets/map/end.png'),
scale: 0.3,
anchor: [0.5, 1],
// rotation: -state.countRotate(),
}),
text: new Text({
text: time,
font: '14px sans-serif',
scale: 1,
offsetY: -50,
placement: 'point',
overflow: true,
fill: new Fill({
color: 'blue',
}),
}),
}),
})
state.map.addLayer(lineLayer)
}
//创建小车这个要素
const moveStart = () => {
//坐标转换 方便计算下一个位置
state.dotsData = state.coordinates.map((item) => {
return transform(item, 'EPSG:3857', 'EPSG:4326')
})
//深复制车的位置,不在原数组改变,方便重新播放
state.carPoints = JSON.parse(JSON.stringify(state.dotsData))
// 当前的小车
state.carPoint = new Feature({
geometry: new Point(fromLonLat(state.carPoints[0])),
})
// 设置小车样式
state.carPoint.setStyle(
new Style({
image: new Icon({
src: require('@/assets/map/ship.png'),
scale: 1,
anchor: [0.5, 0.5],
rotation: -countRotate(),
}),
})
)
let source = new VectorSource({
features: [state.carPoint],
})
state.carLayer = new VectorLayer({
source: source,
zIndex: 999,
})
state.map.addLayer(state.carLayer)
timeStart()
}
//计时器开始
const timeStart = () => {
state.timer = setInterval(() => {
if (state.routeIndex + 1 >= state.carPoints.length) {
//重头开始
state.routeIndex = 0
//移除要素
state.carLayer.getSource().removeFeature(state.carPoint)
clearInterval(state.timer)
//重复运动
addPointAndView(state.coordinates) //自动开启功能
return
}
// 到达下一个点了 需要变化角度
if (nextPoint() === state.carPoints[state.routeIndex + 1]) {
state.routeIndex++
state.carPoint.getStyle().getImage().setRotation(-countRotate())
drawLine(state.newCoordinates)
}
//改变坐标点
state.carPoint.getGeometry().setCoordinates(fromLonLat(state.carPoints[state.routeIndex]))
}, 10)
}
//计算下一个点的位置
//这里的算法是计算了两点之间的点 两点之间的连线可能存在很多个计算出来的点
const nextPoint = () => {
let routeIndex = state.routeIndex
let p1 = state.map.getPixelFromCoordinate(
fromLonLat(state.carPoints[routeIndex])
) //获取在屏幕的像素位置
let p2 = state.map.getPixelFromCoordinate(
fromLonLat(state.carPoints[routeIndex + 1])
)
let dx = p2[0] - p1[0]
let dy = p2[1] - p1[1]
//打印可见 在没有走到下一个点之前,下一个点是不变的,前一个点以这个点为终点向其靠近
let distance = Math.sqrt(dx * dx + dy * dy) * 3
if (distance <= 1) {
return state.carPoints[routeIndex + 1]
} else {
let x = p1[0] + dx / distance
let y = p1[1] + dy / distance
let coor = transform(
state.map.getCoordinateFromPixel([x, y]),
'EPSG:3857',
'EPSG:4326'
)
state.carPoints[routeIndex] = coor //这里会将前一个点重新赋值 要素利用这个坐标变化进行移动
var newCoor = state.map.getCoordinateFromPixel([x, y])
if (newCoor[0] && newCoor[1]) {
var data = [...state.coordinates]
data.splice(0, routeIndex + 1)
state.newCoordinates = [newCoor, ...data]
drawLine(state.newCoordinates)
}
return state.carPoints[routeIndex]
}
}
//计算两点之间的角度 算旋转角度
const countRotate = () => {
let i = state.routeIndex,
j = i + 1
if (j === state.carPoints.length) {
i--
j--
}
let p1 = state.carPoints[i]
let p2 = state.carPoints[j]
// console.log(Math.atan2(p2[0] - p1[0], p2[1] - p1[1]),p2[0] - p1[0],p2[1] - p1[1])
return Math.atan2(p2[1] - p1[1], p2[0] - p1[0])
}
const getData = (data) => {
getTrajectoryApi(data).then((res: any) => {
if (res.code == 0) {
handleData(res.data)
}
})
}
const handleData = (dataInfo: any) => {
var data = dataInfo.gj.map((item) => {
return transform(item, 'EPSG:4326', 'EPSG:3857')
})
state.coordinates = data
addPointAndView(state.coordinates)
drawBtmLine()
var lastPoint = state.coordinates[state.coordinates.length - 1]
drawEnd(lastPoint, dataInfo.time, false)
var firstPoint = state.coordinates[0]
drawStart(firstPoint)
}
// 渔船停靠过程
const handleTrack = (data: any) => {
getShipBerthInfoApi(data).then((res: any) => {
if (res.code == 0) {
ctx.emit("getTimeData", res.data);
handleData(res.data)
drawPort(res.data.shipBerth)
}
})
}
// 绘制停泊区域
const drawPort = (data: any) => {
console.log(data, 'ppppp')
var subject = data
let coord = []
if (subject.geoJson) {
for (let l of JSON.parse(subject.geoJson)) {
if (l.lon > 10000000) {
coord.push(fromLonLat([l.lon / 10000000, l.lat / 10000000], 'EPSG:3857'))
} else {
coord.push(fromLonLat([l.lon, l.lat]))
}
}
}
let feature = new Feature({
geometry: new Polygon([coord]),
})
let source = new VectorSource({
features: [feature],
})
let lineLayer = new VectorLayer({
source: source,
// zIndex: 210,
style: new Style({
stroke: new Stroke({
color: subject.lineColor || 'red',
width: subject.lineWidth || 1,
}),
fill: new Fill({
color: subject.fillColor,
}),
text: new Text({
text: subject.name || '',
font: '10px sans-serif',
scale: 1,
offsetY: subject.type == 1 ? -20 : 0,
placement: 'point',
overflow: true,
fill: new Fill({
color: 'red',
}),
}),
}),
})
state.map.addLayer(lineLayer)
}
const handleEnd = (data: any) => {
getShipBerthInfoEndApi(data).then((res: any) => {
if (res.code == 0) {
ctx.emit("getTimeData", res.data);
drawPort(res.data.shipBerth)
var lastPoint = transform([res.data.lon, res.data.lat], 'EPSG:4326', 'EPSG:3857')
drawEnd(lastPoint, res.data.time, true)
}
})
}
const clear = () => {
clearInterval(state.timer)
let map = state.map
state.shipTrackSource.clear()
map.getLayers().forEach(function(lyr) {
// 检查图层类型是否是地图层
if (lyr.get('isBaseLayer')) {
// 保留地图层
} else {
// 移除其他图层
lyr.getSource().clear()
}
});
}
defineExpose({
drawPort,
getData,
clear,
})
return {
drawPort,
getData,
handleTrack,
handleEnd,
clear,
}
},
})
</script>
说明:coordinates格式
coordinates: [
[121.20256119692851, 27.85807498090228],
[121.19761950502657, 27.8452116688472],
[121.18814081630589, 27.837813949040797],
[121.183046617258, 27.835185998528193],
[121.17734951444675, 27.837927439862685],
[121.17780028082089, 27.839351604582934],
[121.17818682986521, 27.841990178333713],
[121.17547850965846, 27.842725270769535],
],
shipBerth: {
"id": "1661894383035592706",
"geoJson": "[{"lon":1211781670,"lat":278477070},{"lon":1211782190,"lat":278473800},{"lon":1211774570,"lat":278471470},{"lon":1211773700,"lat":278474770}]",
"name": "小型船只停泊区域三",
"shipBerthType": "0",
"regionalCapacity": 30,
"availableRegionalCapacity": 10,
"fillColor": "rgba(0,128,0,1)",
"lineWidth": "1",
"lineColor": "rgba(0,0,255,1)",
"category": "02",
"subCategory": "0223",
"type": "3",
"fontSize": "14",
"fontColor": "rgba(238, 234, 234, 1)",
"resolution": "2.4035558703611457",
"remark": "小型船只停泊区域三",
"deptId": "330000",
"deptName": "浙江省",
"deptPath": "/330000",
"delFlag": "0",
"useType": 1
},