252 lines
5.8 KiB
Vue
252 lines
5.8 KiB
Vue
<template>
|
|
<div ref="mapContainerRef" class="map-container">
|
|
<template v-if="map">
|
|
<slot></slot>
|
|
</template>
|
|
</div>
|
|
</template>
|
|
<script>
|
|
import TileLayer from 'ol/layer/Tile'
|
|
import Map from 'ol/Map'
|
|
import XYZ from 'ol/source/XYZ'
|
|
import View from 'ol/View'
|
|
import { fromLonLat } from 'ol/proj'
|
|
import Stroke from 'ol/style/Stroke'
|
|
import { MarkerIcon, MarkerType } from './markerEnum'
|
|
import Icon from 'ol/style/Icon'
|
|
import VectorSource from 'ol/source/Vector'
|
|
import VectorLayer from 'ol/layer/Vector'
|
|
import { LineString, Point } from 'ol/geom'
|
|
import { Feature } from 'ol'
|
|
import Style from 'ol/style/Style'
|
|
|
|
const mapSourceUrl = process.env.VUE_APP_MAP_BASE_URL
|
|
export default {
|
|
props: {
|
|
zoom: {
|
|
type: Number,
|
|
default: 1,
|
|
},
|
|
maxZoom: {
|
|
type: Number,
|
|
default: 16,
|
|
},
|
|
minZoom: {
|
|
type: Number,
|
|
default: 1,
|
|
},
|
|
center: {
|
|
type: Object,
|
|
default() {
|
|
return {
|
|
longitude: 116,
|
|
latitude: 40,
|
|
}
|
|
},
|
|
},
|
|
},
|
|
data() {
|
|
return {
|
|
map: null,
|
|
stationList: [],
|
|
}
|
|
},
|
|
mounted() {
|
|
this.initMap()
|
|
},
|
|
methods: {
|
|
// 初始化地图
|
|
initMap() {
|
|
const { longitude, latitude } = this.center
|
|
|
|
this.tileLayer = new TileLayer({
|
|
source: new XYZ({
|
|
url: mapSourceUrl,
|
|
}),
|
|
})
|
|
|
|
this.animationSource = new VectorSource({
|
|
features: [],
|
|
})
|
|
|
|
this.animationLayer = new VectorLayer({
|
|
source: this.animationSource,
|
|
})
|
|
|
|
const layers = [this.tileLayer, this.animationLayer]
|
|
|
|
this.view = new View({
|
|
projection: 'EPSG:3857', // 使用这个坐标系
|
|
center: fromLonLat([longitude, latitude]),
|
|
zoom: this.zoom,
|
|
maxZoom: this.maxZoom,
|
|
minZoom: this.minZoom,
|
|
extent: [-20037508.34 - 3500000, -20037508.34, 20037508.34 + 3500000, 20037508.34],
|
|
})
|
|
|
|
this.map = new Map({
|
|
target: this.$refs.mapContainerRef,
|
|
layers,
|
|
view: this.view,
|
|
controls: [],
|
|
})
|
|
},
|
|
|
|
/**
|
|
* 以下是工具方法
|
|
*/
|
|
// 获取地图缩放级别
|
|
getZoom() {
|
|
return this.map.getView().getZoom()
|
|
},
|
|
|
|
// 设置地图缩放级别
|
|
setZoom(zoom) {
|
|
if (zoom < this.minZoom) {
|
|
zoom = this.minZoom
|
|
}
|
|
if (zoom > this.maxZoom) {
|
|
zoom = this.maxZoom
|
|
}
|
|
this.map.getView().animate({ zoom })
|
|
},
|
|
|
|
getMapInstance() {
|
|
return this.map
|
|
},
|
|
|
|
// 获取地图中心点
|
|
getCenter() {
|
|
return this.map.getView().getCenter()
|
|
},
|
|
|
|
// 平移到某个位置
|
|
panTo(center, duration = 1000) {
|
|
return this.map.getView().animate({
|
|
center: fromLonLat(center),
|
|
duration,
|
|
})
|
|
},
|
|
|
|
// 修改地图来源
|
|
changeSource(url) {
|
|
this.tileLayer.setSource(
|
|
new XYZ({
|
|
url,
|
|
})
|
|
)
|
|
},
|
|
|
|
// 按路线运动
|
|
animateByRoute(coordinates) {
|
|
if (!coordinates) {
|
|
return
|
|
}
|
|
|
|
if (!coordinates.length) {
|
|
this.stopRouteAnimation()
|
|
this.removeAnimationFeatures()
|
|
return
|
|
}
|
|
|
|
this.startRouteAnimation(
|
|
coordinates,
|
|
{
|
|
stroke: new Stroke({
|
|
color: '#17f85c',
|
|
lineDash: [6],
|
|
width: 2,
|
|
}),
|
|
},
|
|
{
|
|
image: new Icon({
|
|
src: MarkerIcon[MarkerType.Car],
|
|
anchor: [0.65, 0.5],
|
|
}),
|
|
},
|
|
[100, 430, 100, 450]
|
|
)
|
|
},
|
|
/**
|
|
* 沿轨迹运动
|
|
* @param {Array<any>} route 轨迹路线
|
|
* @param {Object} marker 运动的物体
|
|
*/
|
|
startRouteAnimation(route, lineStyleOptions, markerStyleOptions, padding = []) {
|
|
// 清理上次的动画
|
|
this.stopRouteAnimation()
|
|
|
|
// 清理动画图层
|
|
this.removeAnimationFeatures()
|
|
|
|
const lineString = new LineString(route.map((item) => fromLonLat(item)))
|
|
const extent = lineString.getExtent()
|
|
this.view.fit(extent, {
|
|
padding,
|
|
duration: 1000,
|
|
})
|
|
|
|
// 创建轨迹线
|
|
const routeFeature = new Feature({
|
|
geometry: lineString,
|
|
})
|
|
|
|
// 设置轨迹线样式,并添加到地图上
|
|
routeFeature.setStyle(new Style(lineStyleOptions))
|
|
this.animationSource.addFeature(routeFeature)
|
|
|
|
// 创建标记物,并设置位置到轨迹路线的初始位置
|
|
const markerFeature = new Feature({
|
|
geometry: new Point(lineString.getFirstCoordinate()),
|
|
})
|
|
|
|
// 设置标记物样式,并添加到地图上
|
|
markerFeature.setStyle(new Style(markerStyleOptions))
|
|
this.animationSource.addFeature(markerFeature)
|
|
|
|
let lastTime = Date.now(),
|
|
distance = 0
|
|
this.postRenderCallback = (event) => {
|
|
const speed = 60
|
|
const time = event.frameState.time
|
|
const elapsedTime = time - lastTime
|
|
distance = (distance + (speed * elapsedTime) / 1e6) % 2
|
|
lastTime = time
|
|
|
|
if (distance >= 1) {
|
|
this.stopRouteAnimation()
|
|
return
|
|
}
|
|
|
|
const currentCoordinate = lineString.getCoordinateAt(distance > 1 ? 2 - distance : distance)
|
|
markerFeature.setGeometry(new Point(currentCoordinate))
|
|
|
|
// tell OpenLayers to continue the postrender animation
|
|
this.map.render()
|
|
}
|
|
|
|
this.animationLayer.on('postrender', this.postRenderCallback)
|
|
},
|
|
|
|
// 移除动画层所有元素
|
|
removeAnimationFeatures() {
|
|
this.animationSource.clear()
|
|
},
|
|
|
|
// 停止轨迹动画
|
|
stopRouteAnimation() {
|
|
if (this.postRenderCallback) {
|
|
this.animationLayer.un('postrender', this.postRenderCallback)
|
|
this.postRenderCallback = null
|
|
}
|
|
},
|
|
},
|
|
}
|
|
</script>
|
|
<style lang="less" scoped>
|
|
.map-container {
|
|
height: 100%;
|
|
position: relative;
|
|
}
|
|
</style>
|