<template>
<div class="map" id="map">
<div class="overlay-container" style="display: none">
<!-- 车辆弹窗 -->
<div class="overlay" ref="overlay_truck">
<div class="close" @click="overlay.setPosition(null)">×</div>
<p class="title">{{overlayData.name}}</p>
<p class="content"><span>车牌号码:</span>{{overlayData.number}}</p>
<p class="content"><span>车辆型号:</span>{{overlayData.model}}</p>
<p class="content"><span>车辆状态:</span>{{overlayData.alarm ? '告警' : '正常'}}</p>
<el-row style="text-align: center">
<el-button size="mini" type="primary" @click="onStartAnimationClick(overlayData.id)">查看轨迹</el-button>
<el-button size="mini" type="primary" >导出轨迹</el-button>
</el-row>
</div>
</div>
<!--测试按钮-->
<div class="button-row">
<button @click="locateTruck(1)">车辆1定位</button>
<button @click="onStartAnimationClick(1)">轨迹回放</button>
<br>
<button @click="locateTruck(2)">车辆2定位</button>
<button @click="onStartAnimationClick(2)">轨迹回放</button>
</div>
</div>
</template>
<script>
import "ol/ol.css";
import {Map, Feature, View, Overlay, Graticule} from "ol";
import {Point, Circle as CircleGeom, Polygon, LineString} from "ol/geom";
import {Vector as VectorSource, OSM, XYZ, } from "ol/source";
import {Tile as TileLayer, Vector as VectorLayer} from "ol/layer";
import {Circle, Fill, Stroke, Style, Text, Icon} from "ol/style";
import {getVectorContext} from 'ol/render';
import {Select, Draw} from 'ol/interaction';
import {click} from 'ol/events/condition';
export default {
name: "MapView",
data(){
return{
map: null,
layers: {
pointLayer: null,
traceLayer: null,
},
overlay: null,
overlayData: {},
animating: false,
now: '',
speed: 2,
trace: [],
truckData: [
{
id: 1, //车辆id?
name: '测试卡车1',
alarm: false,
location: [113.399045209149, 23.167922557035595],
trace: [
[113.38481017131984, 23.167432854758143],
[113.3866398986965, 23.166218294344322],
[113.38842230553757, 23.16860009463636],
[113.38878509631053, 23.169940843145127],
[113.3906148236872, 23.169467637789094],
[113.39223949540958, 23.169514958324697],
[113.39370643201329, 23.16984620207392],
[113.39474748379656, 23.1700512577282],
[113.39525223617633, 23.170114351775673],
[113.39552038587809, 23.170098578263804],
[113.39626174093587, 23.17008280475194],
[113.39684536087499, 23.170019710704466],
[113.39728701920728, 23.169956616656997],
[113.39744475432596, 23.16984620207392],
[113.39771290402771, 23.1695622788603],
[113.39774445105145, 23.167937607137915],
[113.399045209149, 23.167922557035595]
]
},
{
id: 2, //车辆id?
name: '测试卡车2',
alarm: true,
location: [113.38432928536788, 23.143003626954737],
trace: [
[113.36545258773141, 23.12866953003676],
[113.36638589917159, 23.12843077594741],
[113.36625470009082, 23.127797973659423],
[113.36859282430896, 23.127278390499832],
[113.36980974276169, 23.1299173260209],
[113.37011055406465, 23.131476075499663],
[113.3711223739018, 23.13564641401742],
[113.37058911750121, 23.137150470532028],
[113.37038401888559, 23.138011884717663],
[113.37042503860872, 23.138394735466843],
[113.37110870066086, 23.141334482290844],
[113.37348784460212, 23.14052776106938],
[113.37400742776171, 23.141170403398345],
[113.37562087020464, 23.144014437535038],
[113.3757986223382, 23.143891378365662],
[113.37750777746841, 23.143549547339617],
[113.37790430145863, 23.143535874098585],
[113.37961345658886, 23.14394607132984],
[113.37999630733802, 23.144137496704424],
[113.382279738592, 23.143399141688167],
[113.38234810479722, 23.143563220580667],
[113.38432928536788, 23.143003626954737]
]
}
]
}
},
methods: {
/***
* 初始化地图
*/
initMap(){
this.map = new Map({
target: "map",
layers: [
new TileLayer({
source: new XYZ({
url: 'http://t2.tianditu.com/DataServer?T=vec_w&x={x}&y={y}&l={z}&tk=ce70a52426fabd6c2062fdd14c3426d2',
crossOrigin: 'anonymous'
})
}),
new TileLayer({
source: new XYZ({
url: 'http://t2.tianditu.com/DataServer?T=cia_w&x={x}&y={y}&l={z}&tk=469cfd9c133f30baaf3f94a9cd848c47',
crossOrigin: 'anonymous'
})
}),
],
view: new View({
center: [113.3, 23.1],
zoom: 12,
projection: 'EPSG:4326',
}),
interactions: null,
});
},
/**
* 初始化图层
* */
initLayers(){
//点位图层
this.layers.pointLayer = new VectorLayer({
source: new VectorSource(),
renderMode: "vector",
updateWhileInteracting: true,
updateWhileAnimating: true
});
this.map.addLayer(this.layers.pointLayer);
this.layers.pointLayer.setZIndex(200);
//轨迹图层
this.layers.traceLayer = new VectorLayer({
source: new VectorSource(),
renderMode: "vector",
updateWhileInteracting: true,
updateWhileAnimating: true
});
this.map.addLayer(this.layers.traceLayer);
this.layers.traceLayer.setZIndex(150);
//加点
this.addTruckPoint();
//点位选中
const clickSelect = new Select({
condition: click,
layers: [this.layers.pointLayer]
});
clickSelect.on('select', (e)=> {
const features = e.selected;
features.forEach((feature)=>{
this.onPointClick(feature);
clickSelect.getFeatures().clear();
});
});
this.map.addInteraction(clickSelect);
},
/***
* 初始化点位窗口
*/
initOverlay(){
this.overlay = new Overlay({
element: this.$refs.overlay_truck,
autoPan: true,
autoPanAnimation: {
duration: 250
},
});
this.map.addOverlay(this.overlay);
},
/***
* 加点
*/
addTruckPoint(){
this.truckData.forEach(item=>{
const point = new Feature({
geometry: new Point(item.location),
type: "point"
});
const colors = item.alarm ? ['#ff5858', '#860700']: ['#00ff61', '#008635']; //是否告警
point.setStyle(new Style({
image: new Circle({
fill: new Fill({
color: colors[0]
}),
stroke: new Stroke({
color: colors[1],
width: 1.25
}),
radius: 10
}),
}));
point.setProperties(item); //点位数据
point.setId(item.id); //点位id,用来对应列表点击事件
this.layers.pointLayer.getSource().addFeature(point);
});
},
/***
* 根据id定位车辆,触发点击事件
* @param id
*/
locateTruck(id){
const feature = this.layers.pointLayer.getSource().getFeatureById(id);
if(feature){
this.onPointClick(feature);
}
},
/***
* 车辆点位的点击事件
* @param feature
*/
onPointClick(feature){
const attributes = feature.getProperties();
//弹出窗
this.overlay.setPosition(attributes.geometry.flatCoordinates);
this.overlayData = attributes;
//地图移动缩放
this.map.getView().setZoom(16);
this.panToCoordinate(attributes.geometry.flatCoordinates);
},
/***
* 开始轨迹回放的事件
* @param id
*/
onStartAnimationClick(id){
this.truckData.forEach(item=>{
if(id === item.id){
this.loadTrace(item.trace);
this.startAnimation();
}
});
},
/***
* 读取轨迹数据,生成轨迹图层中的feature
* @param trace
*/
loadTrace(trace){
this.trace = trace;
this.layers.traceLayer.getSource().clear(); //清除上一次回放数据
let lineString = new LineString(trace);
let traceLineString = new Feature({
type: 'lineString',
geometry: lineString
});
traceLineString.setStyle(new Style({
stroke: new Stroke({
width: 4, color: [0, 100, 200, 0.8]
})
}));
this.layers.traceLayer.getSource().addFeature(traceLineString);
},
/***
* 地图移动
* @param coordinate
*/
panToCoordinate(coordinate){
this.map.getView().animate({
center: coordinate,
duration: 500
});
},
/***
* 轨迹点位移动事件
* @param event
*/
moveFeature(event){
const vectorContext = getVectorContext(event);
const frameState = event.frameState;
if (this.animating) {
const elapsedTime = frameState.time - this.now;
const index = Math.round(this.speed * elapsedTime / 1000);
if (index >= this.trace.length) {
this.stopAnimation(true);
return;
}
const currentPoint = new Point(this.trace[index]);
const feature = new Feature(currentPoint);
vectorContext.drawFeature(feature, new Style({
image: new Circle({
fill: new Fill({
color: 'dodgerblue'
}),
stroke: new Stroke({
color: 'white',
width: 1.25
}),
radius: 8
})
}));
this.panToCoordinate(currentPoint.getCoordinates())
}
// tell OpenLayers to continue the postrender animation
this.map.render();
},
/***
* 开始轨迹回放
*/
startAnimation(){
if (this.animating) {
this.stopAnimation(false);
} else {
this.animating = true;
this.now = new Date().getTime();
this.layers.traceLayer.on('postrender', this.moveFeature);
this.map.render();
}
},
/***
* 停止轨迹回放
*/
stopAnimation(){
this.animating = false;
this.layers.traceLayer.getSource().clear();
}
},
mounted(){
this.initMap();
this.initOverlay();
this.initLayers();
}
}
</script>
<style scoped lang="scss">
.map{
position: absolute;
top: 0;
bottom: 0;
right: 0;
left: 0;
.button-row{
position: absolute;
top: 8px;
left: 16px;
z-index: 200;
button{
font-size: 16px;
background: white;
padding: 8px 16px;
margin-bottom: 16px;
}
}
.overlay{
width: 360px;
height: 180px;
background: rgba(0,0,0,0.8);
color: white;
position: relative;
text-align: left;
padding: 16px 8px 0 8px;
margin-left: -180px;
margin-top: -200px;
&::after{
content: "";
display: block;
position: absolute;
top: 180px;
left: calc(180px - 6px);
border-top: 8px solid rgba(0,0,0,0.8);
border-left: 6px solid transparent;
border-right: 6px solid transparent;
border-bottom: 6px solid transparent;
}
.close{
position: absolute;
padding: 0 8px;
right: 0;
top: 0;
font-size: 20px;
cursor: pointer;
}
.title{
font-weight: bold;
margin-top: 0;
}
.content{
margin: 8px 0;
span{
display: inline-block;
width: 5.5em;
}
}
.button-row{
margin: 0 auto;
}
}
}
</style>