实现多多车辆实时轨迹监控,每个10秒监测一次接口,进行动态追加。(前端实现,如果数量庞大的车辆移动,很消耗性能)
百度离线地图引入方法查看: vue项目实现百度离线地图开发
<template>
<div class="app-container">
<el-row :gutter="24">
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<div class="head-container">
<div id="map" class="baiduMap"></div>
<el-tooltip content="刷新地图" placement="bottom">
<el-button
class="refresh"
type="primary"
icon="el-icon-refresh"
size="mini"
circle
@click="onClickRefreshMap"
></el-button>
</el-tooltip>
</div>
</el-col>
</el-row>
</div>
</template>
<script>
import $ from "jquery";
export default {
name: "MapShows",
components: {},
data() {
return {
markerArr: [],
orgId: "",
map: null,
polyline: null,
deviceCode: null,
centrePoint: [],
mobileObj: {},
intervalArr: [],
firstTime: true,
intervalMap: null,
markerNewArr: [],
markerGather: [],
};
},
created() {},
mounted() {
this.getMapZfysByOrgId();
},
methods: {
//初始化页面信息
getMapZfysByOrgId() {
let self = this;
self.$api.getMapZfysByOrgIds().then((response) => {
if (self.firstTime) {
self.markerArr = response;
//增量轨迹坐标
self.intervalMap = setInterval(function () {
self.$api.getDeviceTrack().then((response) => {
let points = response;
//循环初始化点
for(let i = 0;i < self.markerArr.length; i++) {
//循环增量点
for(let j = 0;j < points.length; j++) {
// 判断id一样就往初始化点插入增量坐标参数
if (self.markerArr[i].deviceCode == points[j].id) {
// 循环id相同下的增量坐标数组
for(let k = 0;k < points[j].tracks.length; k++) {
// 插入增量数组
self.markerArr[i].tracks.push(points[j].tracks[k]);
}
}
}
}
});
}, 10000);
// 使用初始化点坐标返回的轨迹坐标
self.initMap(self.markerArr);
} else {
// 使用增量插入的轨迹坐标
self.markerNewArr = response;
self.markerNewArr.forEach((e) => {
self.markerArr.push(e);
});
self.initMap(self.markerNewArr);
}
});
},
//初始化地图信息
initMap(markerArr) {
let self = this;
if (self.firstTime) {
self.firstTime = false;
self.$api.getAllByDictionaryName("MapSetting").then((response) => {
// 获取系统地图参数设置
const mapSetting = response.items;
let defaultCenterX = 87.36,
defaultCenterY = 43.45,
defaultLevel = 12,
maxLevel = 16,
minLevel = 4;
mapSetting.forEach((m) => {
if (m.label == "默认中心点" && m.value) {
let point = m.value.split(";");
defaultCenterX = parseFloat(point[0]);
defaultCenterY = parseFloat(point[1]);
} else if (m.label == "默认层级" && m.value) {
defaultLevel = parseInt(m.value);
} else if (m.label == "最大层级" && m.value) {
maxLevel = parseInt(m.value);
} else if (m.label == "最小层级" && m.value) {
minLevel = parseInt(m.value);
}
});
//渲染地图
self.$nextTick(() => {
self.map = new BMap.Map("map", {
mimZoom: minLevel,
maxZoom: maxLevel,
}); //写入地图div的id名称
var point = new BMap.Point(defaultCenterX, defaultCenterY); // 创建点坐标
self.map.centerAndZoom(point, defaultLevel); // 初始化地图,设置中心点坐标和地图级别
self.map.enableScrollWheelZoom();
self.map.enableScrollWheelZoom(true); // 激活滚轮操作
self.map.addControl(
new BMap.MapTypeControl({
//添加地图类型控件
mapTypes: [BMAP_NORMAL_MAP, BMAP_HYBRID_MAP],
})
);
self.map.setMapType(BMAP_HYBRID_MAP); //设置卫星地图
//添加控件
var ctrlNav = new window.BMap.NavigationControl({
anchor: BMAP_ANCHOR_TOP_LEFT,
type: BMAP_NAVIGATION_CONTROL_LARGE,
});
self.map.addControl(ctrlNav); //缩放
var ctrlOva = new window.BMap.OverviewMapControl({
anchor: BMAP_ANCHOR_BOTTOM_RIGHT,
isOpen: 1,
});
self.map.addControl(ctrlOva); //缩略图
var ctrlSca = new window.BMap.ScaleControl({
anchor: BMAP_ANCHOR_BOTTOM_LEFT,
});
self.map.addControl(ctrlSca); //比例尺
self.mapPublic(markerArr);
});
});
} else if (!self.firstTime && self.map != null) {
self.mapPublic(markerArr);
}
},
//每个点的样式和方法实现
mapPublic(markerArr) {
let self = this;
markerArr.forEach((e, i) => {
// 创建point, 将x,y值传入
let pointNumber = new BMap.Point(e.longitude, e.latitude);
let deviceCode = e.deviceCode;
// 将data中的name加入地图中
var label = new BMap.Label(e.name, {
offset: new BMap.Size(0, -16),
});
label.setStyle({
color: "#fff", //字体颜色
fontSize: "16px", //字体大小
backgroundColor: "#1890FF", //文本标注背景颜色
border: "none",
// fontWeight :"bold" //字体加粗
});
let imgSrc = self.typeDeviceImg(e.taskLevel);
// let imgSrc = e.taskLevel
// ? require("../../../assets/image/map/zaixian.png")
// : require("../../../assets/image/map/buzaixian.png");
var myIcon = new BMap.Icon(
require("../../../assets/image/map/bjd.png"),
new BMap.Size(36, 36),
{
anchor: new BMap.Size(20, 45), //这句表示图片相对于所加的点的位置mapStart
imageSize: new BMap.Size(45, 45), //图标所用的图片的大小,此功能的作用等同于CSS中的background-size属性。可用于实现高清屏的高清效果
// offset: new BMap.Size(-10, 45), // 指定定位位置
// imageOffset: new BMap.Size(0, 0 - 10 * 25) // 设置图片偏移
}
);
self.$set(self.mobileObj, "mobile" + e.name, {});
markerFun(pointNumber, label, deviceCode, myIcon);
self.realTimeTrack(pointNumber,e.deviceType,i,e.taskLevle,deviceCode,label,e.tracks,self.map);
});
function markerFun(points, label, deviceCode, myIcon) {
// let self = this;
let markers = new BMap.Marker(points, { icon: myIcon });
markers.enableDragging();
markers.name = "line";
//标记点name显示在地图上
// markers.setLabel(label); // 将data中的name添加到地图中
label.setStyle({
// 设置label的样式
color: "#fff",
fontSize: "14px",
border: "none",
borderRadius: "5px",
});
var curve = new BMapLib.CurveLine(points, {
strokeColor: "blue",
strokeWeight: 3,
strokeOpacity: 0.5,
}); //创建弧线对象
// self.map.addOverlay(curve); //添加到地图中
// 标注的点击事件
// markers.addEventListener("click", function (event) {
// self.getZfyDetail(deviceCode, points);
// });
// self.map.addOverlay(markers); // 将标注添加到地图中
self.markerGather.push(markers);
// self.map.addOverlay(markers);
if (self.centrePoint.length > 0) {
self.map.setViewport(self.centrePoint);
}
}
this.markerClusterer(this.markerGather);
},
//点聚合实现方法
markerClusterer(markerGather) {
//最简单的用法,生成一个marker数组,然后调用markerClusterer类即可。
let markerClusterer = new window.BMapLib.MarkerClusterer(this.map, {
markers: markerGather,
styles: [
{
url: require("../../../assets/image/map/quyu1.png"), //点聚合图标背景图
size: new BMap.Size(27, 43), //点聚合图标大小
textColor: "#fff",
},
],
});
},
//实时轨迹
realTimeTrack(points,deviceType,i,taskLevle,deviceCode,label,tracks,map) {
let self = this;
let PointArr = tracks;
let mobile_devices = deviceType;
self.addStartMarker(
new BMap.Point(PointArr[0].lng, PointArr[0].lat),
"起点",
self.map
);
//设备行驶图标
var j = 0;
// 初始化加载旧的轨迹路线
for(let i = 0;i < PointArr.length; i++) {
if (i + 1 < PointArr.length && PointArr != []) {
self.drowLine(
self.map,
PointArr[i],
PointArr[i + 1],
mobile_devices,
taskLevle,
deviceCode,
label
); //画线调用
}
}
// 因为初始化加载可能存在设备已经在线,所以拿到旧的轨迹,在进行追加坐标吗,从而达到实时全轨迹,j是旧的坐标数组最后一个下标
j = PointArr.length - 1
// 轨迹画线定时器
var interval = setInterval(function () {
if (j + 1 < PointArr.length && PointArr != []) {
self.drowLine(
self.map,
PointArr[j],
PointArr[j + 1],
mobile_devices,
taskLevle,
deviceCode,
label
); //画线调用
j = j + 1;
}
},100);
self.intervalArr.push(interval);
},
//实时轨迹颜色
taskLevleColorStyle(name) {
switch (name) {
case "1":
return "#E34D59";
case "2":
return "#F17E4B";
case "3":
return "#E5C80D";
case "4":
return "#1890FF";
case "5":
return "#00BF87";
default:
return "#1890FF";
}
},
//移动设备图标
typeDeviceImg(name) {
// 0,执法仪 ;1,无人机;2,布控球
switch (name) {
case 0:
return require("../../../assets/image/map/zfy.png");
case 1:
return require("../../../assets/image/map/wrj.png");
default:
return require("../../../assets/image/map/bkq.png");
}
},
// 设备划线
drowLine(map,PointArr,PointArrNext,mobile_devices,taskLevle,deviceCode,label) {
let self = this;
if (PointArrNext != undefined) {
var polyline = new BMap.Polyline(
[
new BMap.Point(PointArr.lot, PointArr.lat),
new BMap.Point(PointArrNext.lot, PointArrNext.lat),
],
{
strokeColor: self.taskLevleColorStyle(taskLevle),
strokeWeight: 7,
strokeOpacity: 1,
}
); //创建折线
polyline.name = "line";
self.map.addOverlay(polyline);
self.addMarkerEnd(
new BMap.Point(PointArrNext.lot, PointArrNext.lat),
"小车行驶",
map,
PointArrNext,
new BMap.Point(PointArr.lot, PointArr.lat),
mobile_devices,
deviceCode,
label
); //添加图标
} else {
self.addMarkerEnd(
new BMap.Point(PointArr.lot, PointArr.lat),
"终点",
map,
mobile_devices,
deviceCode,
label
); //添加终点图标
}
},
//添加起始图标
addStartMarker(point, name, mapInit) {
if (name == "起点") {
var myIcon = new BMap.Icon(
require("../../../assets/image/map/bjd.png"),
new BMap.Size(36, 36),
{
anchor: new BMap.Size(20, 45), //这句表示图片相对于所加的点的位置mapStart
imageSize: new BMap.Size(45, 45), //图标所用的图片的大小,此功能的作用等同于CSS中的background-size属性。可用于实现高清屏的高清效果
// offset: new BMap.Size(-10, 45), // 指定定位位置
// imageOffset: new BMap.Size(0, 0 - 10 * 25) // 设置图片偏移
}
);
let marker = new BMap.Marker(point, { icon: myIcon }); // 创建标注
// window.marker = new BMap.Marker(point); // 创建标注
marker.name = "line";
this.map.addOverlay(marker); // 将标注添加到地图中
// window.marker.setAnimation(BMAP_ANIMATION_BOUNCE); //跳动的动画
}
},
//添加行驶和终点图标
addMarkerEnd(point,name,mapInit,trackUnit,prePoint,mobile_devices,deviceCode,label) {
let self = this;
//始点图标
let drivingPoint = new BMap.Icon(
self.typeDeviceImg(mobile_devices),
new BMap.Size(36, 36),
{
anchor: new BMap.Size(27, 13),
imageSize: new BMap.Size(36, 36),
}
);
//终点图标
let terminalPoint = new BMap.Icon(
"http://developer.baidu.com/map/jsdemo/img/car.png",
new BMap.Size(36, 36),
{
anchor: new BMap.Size(20, 45),
imageSize: new BMap.Size(45, 45),
}
);
if (name == "小车行驶") {
if (self.mobileObj["mobile" + mobile_devices]) {
//先判断第一次进来的时候这个值有没有定义,有的话就清除掉上一次的。然后在进行画图标。第一次进来时候没有定义也就不走这块,直接进行画图标
mapInit.removeOverlay(self.mobileObj["mobile" + mobile_devices]);
}
// 设置坐标点图标可拖拽
self.mobileObj["mobile" + mobile_devices] = new BMap.Marker(point, {
icon: drivingPoint,
}); // 创建标注
self.mobileObj["mobile" + mobile_devices].enableDragging();
self.mobileObj["mobile" + mobile_devices].setRotation(trackUnit.route); //trackUnit.route
//getAngle(point,prePoint);// js求解两点之间的角度
self.mobileObj["mobile" + mobile_devices].setRotation(
self.getAngle(point, prePoint) - 90
); // 旋转的角度
//carMk.setAnimation(BMAP_ANIMATION_BOUNCE); //跳动的动画
self.mobileObj["mobile" + mobile_devices].addEventListener(
"click",
function (event) {
self.getZfyDetail(deviceCode, point);
}
);
self.mobileObj["mobile" + mobile_devices].setLabel(label); // 将data中的name添加到地图中
label.setStyle({
// 设置label的样式
color: "#fff",
fontSize: "14px",
border: "none",
borderRadius: "5px",
});
self.mobileObj["mobile" + mobile_devices].name = "line";
mapInit.addOverlay(self.mobileObj["mobile" + mobile_devices]); // 将标注添加到地图中
} else {
// mapInit.removeOverlay(self.mobileObj["mobile" + mobile_devices]);
// self.mobileObj['mobile'+mobile_devices] = new BMap.Marker(point,{icon:drivingPoint}); // 创建标注
// self.mobileObj["mobile" + mobile_devices].setRotation(
// self.getAngle(point, prePoint) - 90
// ); // 旋转的角度
// mapInit.addOverlay(self.mobileObj["mobile" + mobile_devices]);
}
},
//获得角度的函数
getAngle(n, next) {
if (next != undefined) {
var ret;
var w1 = (n.lat / 180) * Math.PI;
var j1 = (n.lng / 180) * Math.PI;
var w2 = (next.lat / 180) * Math.PI;
var j2 = (next.lng / 180) * Math.PI;
ret =
4 * Math.pow(Math.sin((w1 - w2) / 2), 2) -
Math.pow(Math.sin((j1 - j2) / 2) * (Math.cos(w1) - Math.cos(w2)), 2);
ret = Math.sqrt(ret);
// var temp = Math.sin(Math.abs(j1 - j2) / 2) * (Math.cos(w1) + Math.cos(w2));
var temp = Math.sin((j1 - j2) / 2) * (Math.cos(w1) + Math.cos(w2));
// console.log(temp);
ret = ret / temp;
ret = (Math.atan(ret) / Math.PI) * 180;
ret += 90;
// 这里用如此臃肿的if..else是为了判定追踪单个点的具体情况,从而调整ret的值
if (j1 - j2 < 0) {
// console.log('j1
if (w1 - w2 < 0) {
// console.log('w1
ret;
} else {
// console.log('w1>w2')
ret = -ret + 180;
}
} else {
// console.log('j1>j2')
if (w1 - w2 < 0) {
// console.log('w1
ret = 180 + ret;
} else {
// console.log('w1>w2')
ret = -ret;
}
}
return ret;
}
},
// 重新加载删除轨迹图层不在线的设备图层,这里需要跟每次渲染涂层的时候添加一个name才能正常捕获到要删除的图层
removeLayers() {
let array = [];
array.forEach((i, n) => {
array.forEach((j, e) => {
if (n.name !== e.name) {
var allOverlay = this.map.getOverlays();
allOverlay.map((item) => {
if (item.name === n.name) {
this.map.removeOverlay(item);
}
});
array.splice(1, i);
}
});
});
},
//坐标点点击弹框信息展示详情信息
getZfyDetail(deviceCode, points) {
let that = this;
let infoWindow = null;
let dtailObj = null;
this.$api.getZfyDetail(deviceCode).then((response) => {
dtailObj = response;
var opts = {
width: 700, // 信息窗口宽度
height: 240,
title: "设备详情", // 信息窗口标题
};
infoWindow = new window.BMap.InfoWindow(
"执法仪编号:" +
dtailObj.deviceCode +
"
" +
"最后开机时间:" +
(dtailObj.lastPowerOnTime || "") +
"
" +
"最后关机时间:" +
(dtailObj.lastPowerOffTime || "") +
"
" +
"存储信息:" +
(dtailObj.storeInfo || "") +
"
" +
"录像信息:" +
(dtailObj.recordStatus || "") +
"
" +
"电量信息:" +
(dtailObj.powerInfo || "") +
"
" +
"作业任务名称:" +
(dtailObj.workTaskName || "") +
"
" +
"工作负责人:" +
(dtailObj.fzr || "") +
"
" +
"工作开始时间:" +
(dtailObj.startTime || "") +
"
" +
"工作结束时间:" +
(dtailObj.endTime || "") +
"
" +
"" +
"",
opts
); //悬浮提示信息
this.map.openInfoWindow(infoWindow, points);
this.$nextTick(() => {
setTimeout(() => {
// 轨迹
document
.getElementsByClassName("gdClick")[0]
.addEventListener("click", function (ev) {
let deviceCode = $(this).data("devicecode");
that.$api.getZfyDayLocus(deviceCode).then((response) => {
if (that.polyline != null) {
var allOverlay = that.map.getOverlays();
for (var i = 0; i < allOverlay.length; i++) {
if (allOverlay[i].toString().indexOf("Label") > 0) {
//删除折线
that.map.removeOverlay(allOverlay[i]);
}
if (allOverlay[i].toString().indexOf("Polyline") > 0) {
//删除折线
that.map.removeOverlay(allOverlay[i]);
}
}
}
let zfyDayLocusList = response;
let pointNumber = [];
var label = null;
zfyDayLocusList.forEach((e, i) => {
// 创建point, 将x,y值传入
let pointNum = new BMap.Point(e.longitude, e.latitude);
pointNumber.push(pointNum);
//轨迹时间
label = new BMap.Label(e.updateTime, {
// 创建文本标注
position: {
lng: e.longitude,
lat: e.latitude,
title: "gj",
}, // 设置标注的地理位置
offset: new BMap.Size(10, 20), // 设置标注的偏移量
});
that.map.addOverlay(label);
label.setStyle({
// 设置label的样式
color: "#44cef6",
fontSize: "12px",
border: "none",
});
});
// 画图标
// for (var i = 0, j = pointNumber.length; i < j; i++) {
// var marker = new BMap.Marker(pointNumber[i], {
// icon: icon,
// offset: offset
// }); // 创建标注
// map.addOverlay(marker);
// }
that.polyline = new BMap.Polyline(pointNumber, {
strokeColor: "blue",
strokeWeight: 6,
strokeOpacity: 0.5,
});
that.map.addOverlay(that.polyline);
var allOverlay = that.map.getOverlays();
});
});
//播放视频
document
.getElementsByClassName("codeVdeo")[0]
.addEventListener("click", function (ev) {
});
//清除单个轨迹
document
.getElementsByClassName("clear_path")[0]
.addEventListener("click", function (ev) {
var allOverlay = that.map.getOverlays();
// that.map.removeOverlay(allOverlay[allOverlay.length-1]);
for (var i = 0; i < allOverlay.length - 1; i++) {
if ("point" in allOverlay[i]) {
if (
allOverlay[i].point != null &&
allOverlay[i].point != {}
) {
if (
allOverlay[i].point.title == "gj" &&
"title" in allOverlay[i].point
) {
that.map.removeOverlay(allOverlay[i]);
that.map.removeOverlay(
allOverlay[allOverlay.length - 1]
);
}
}
}
}
});
}, 500);
});
});
},
handleRowClick(row, column, event) {
this.$refs.multipleTable.clearSelection();
this.$refs.multipleTable.toggleRowSelection(row);
},
onlineStatus(row) {
this.$api.GetgpsByDeviceCode(row.devicePlayCode).then((response) => {
// var overlays = [new BMap.Point(response.longitude, response.latitude)];
this.centrePoint = [
new BMap.Point(response.longitude, response.latitude),
];
this.map.setViewport(this.centrePoint);
// this.getMapZfysByOrgId();
});
},
//刷新地图
onClickRefreshMap() {
let self = this;
for (let i = 0; i < self.intervalArr.length; i++) {
clearInterval(this.intervalArr);
}
this.intervalArr = [];
this.centrePoint = [];
this.markerArr = [];
this.getMapZfysByOrgId();
},
},
directives: {
drag: {
// 指令的定义
bind: function (el) {
let oDiv = el; // 获取当前元素
oDiv.onmousedown = (e) => {
let disX = e.clientX - oDiv.offsetLeft;
let disY = e.clientY - oDiv.offsetTop;
document.onmousemove = (e) => {
// 用鼠标的位置减去鼠标相对元素的位置,得到元素的位置
let left = e.clientX - disX;
let top = e.clientY - disY;
oDiv.style.left = left + "px";
oDiv.style.top = top + "px";
};
document.onmouseup = (e) => {
document.onmousemove = null;
document.onmouseup = null;
};
return false;
};
},
},
},
};
</script>
<style lang="scss" scoped>
/*引入地图样式*/
@import url("../../../../public/baiduMap/css/baidu_map_v2.css");
.app-container {
padding: 0px;
}
.baiduMap {
height: 820px;
overflow-y: auto;
}
::v-deep .el-tree {
// height: 780px;
background: #f7f7f7;
position: relative;
cursor: default;
color: #606266;
}
.head-container {
position: relative;
.refresh {
position: absolute;
top: 8px;
right: 94px;
padding: 5px;
}
.screen {
position: absolute;
top: 2px;
right: 106px;
padding: 5px;
}
}
.zfy_down {
background: #fff;
position: absolute;
top: 40px;
right: 5px;
border-radius: 6px;
width: 540px;
.zfy_down_list {
width: 540px;
}
}
.zfy_down_query_f {
background: #fff;
position: absolute;
top: 40px;
right: 5px;
border-radius: 6px;
width: 100px;
.query_box{
display: flex;
padding: 2px 4px;
.query_title{
margin: auto;
}
.query_img{
margin-top: 4px;
}
}
}
::v-deep p {
margin: 8px 0;
}
::v-deep a:hover {
color: red;
background: #fff;
text-decoration: none;
}
::v-deep a {
color: #0066cc;
text-decoration: underline;
}
::v-deep .el-icon-video-camera {
color: #0066cc;
margin-left: 10px;
}
::v-deep .el-icon-video-camera:hover {
color: red;
margin-left: 10px;
}
::v-deep .code-sty {
color: #0066cc;
margin-left: 10px;
text-decoration: underline;
}
::v-deep .code-sty:hover {
color: red;
background: #fff;
text-decoration: none;
}
::v-deep .codeVdeo {
cursor: pointer;
display: inline-block;
}
::v-deep .formMap {
text-align: center;
.el-form-item {
margin-bottom: 0;
}
}
//隐藏百度地图左下角图标和文字
::v-deep .anchorBL a {
display: none;
}
::v-deep .anchorBL img {
display: none;
}
::v-deep .anchorBL span {
display: none !important;
}
::v-deep .odd_div {
width: 49%;
border-bottom: 1px solid #eee;
border-right: 1px solid #eee;
}
::v-deep .even_div {
width: 49%;
border-bottom: 1px solid #eee;
padding-left: 8px;
}
</style>
完成改功能效果需要两个接口,①初始化坐标点接口,②增量坐标点接口
[
{
"deviceCode": "37050000001320000090",
"name": "11111111222",
"taskId": null,
"longitude": 113.38011471963698,
"latitude": 23.129711801602145,
"orgId": "00000000-0000-0000-0000-000000000000",
"isOnline": false,
"taskLevel": "5",
"deviceType": 1,
"tracks": null,
"id": "3a07ab99-c4fe-749a-33a9-6cf25e324f76"
}
]
[
{
"id": "3a07ab99-c4fe-749a-33a9-6cf25e324f76",
"tracks": [
{
"lat": 113.38011471963698,
"lot": 23.129711801602145,
}
],
}
]
有个缺点就是如果数据量庞大会导致浏览器性能消耗严重,因为这里使用了定时器实现,应该有更好的办法实现,