vue结合高德地图JS API2.0实现地图路线展示、轨迹纠偏、聚合点、聚合点点击以及弹窗、车辆移动
地图
<template>
<div style="width: 100%; height: 100%">
<div id="map-container"></div>
<div style="display: none">
<MarkerInfo
:roadList="roadList"
:infoContent="nowClusterType"
ref="MarkerInfo"
/>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent, ref, nextTick } from 'vue';
import { loadAMap, mapStyle } from '@/utils/amap';
import { highway } from '@/api';
import coordtransform from 'coordtransform';
import MarkerInfo from './MarkerInfo.vue';
//import * as turf from '@turf/turf';
export default defineComponent({
name: 'Map',
props: {
roadRecord: {
default: [],
},
sickList: {
default: [],
},
sickId: {
default: '',
type: String,
},
roadList: {
default: [],
},
},
components: { MarkerInfo },
setup(prop: any) {
const map = ref<any>(null);
const record = ref<any>({
line: null,
start: null,
end: null,
car: null,
});
const roadList = ref<any>([]);
const fakeMarker = ref<any>(null);
nextTick(() => {
loadAMap()
.then((AMap: any) => {
AMap.plugin('AMap.MoveAnimation', () => {
map.value = new AMap.Map('map-container', {
mapStyle: mapStyle,
resizeEnable: true,
zoom: 12,
rotateEnable: true,
showLabel: true,
pitchEnable: true,
});
//层级变换,清空窗体信息
var logMapChange = function () {
map.value.clearInfoWindow();
if (fakeMarker.value) map.value.remove(fakeMarker.value);
};
map.value.on('zoomchange', logMapChange);
map.value.on('click', logMapChange);
});
})
.finally(() => {
getRoad();
});
});
const getRoad = () => {
highway
.getRouteLineInfo()
.then((res: any) => {
roadList.value = [];
//轨迹纠偏插件引入
window.AMap.plugin('AMap.GraspRoad', () => {
var grasp = new window.AMap.GraspRoad();
res.list.forEach((item: any) => {
let path: any = [],
nowIndex: number = 0;
//减少点的获取,高德只允许传参小于500个
item.forEach((items: any, index: number) => {
if (
((item.length < 5000 && index % 10 === 0) ||
(item.length >= 5000 && item.length < 10000 && index % 20 === 0) ||
(item.length >= 10000 && item.length < 50000 && index % 100 === 0) ||
(item.length >= 50000 && index % 200 === 0)) &&
path.length < 500
) {
if (path.length === 0) {
path.push({
x: coordtransform.wgs84togcj02(items.longitude, items.latitude)[0],
y: coordtransform.wgs84togcj02(items.longitude, items.latitude)[1],
sp: items.speed,
ag: items.heading,
tm: items.gpsTime,
});
} else {
nowIndex++;
path.push({
x: coordtransform.wgs84togcj02(items.longitude, items.latitude)[0],
y: coordtransform.wgs84togcj02(items.longitude, items.latitude)[1],
sp: 10,
ag: 0,
tm: nowIndex,
});
}
}
});
//轨迹纠偏
grasp.driving(path, (error: any, result: any) => {
if (!error) {
var path2 = [];
var newPath = result.data.points;
for (var i = 0; i < newPath.length; i += 1) {
path2.push([newPath[i].x, newPath[i].y]);
}
var newLine = new window.AMap.Polyline({
path: path2,
strokeWeight: 3,
strokeOpacity: 1,
strokeColor: '#00FF67',
});
roadList.value.push(newLine);
map.value.add(newLine);
map.value.setFitView(roadList.value);
}
});
});
});
})
.finally(() => {
setTimeout(() => {
if (prop.sickList && prop.sickList.length > 0)
setCluster();//添加JSAPI2.0下的聚合点
}, 400);
});
};
//清空地图
const removeRecord = (needRemoveLine: boolean = true) => {
map.value?.clearInfoWindow();
if (fakeMarker.value) map.value.remove(fakeMarker.value);
if (needRemoveLine) {
roadList.value.forEach((item: any) => {
if (item) map.value.remove(item);
});
}
if (record.value.line) map.value.remove(record.value.line);
if (record.value.start) map.value.remove(record.value.start);
if (record.value.end) map.value.remove(record.value.end);
if (cluster.value) cluster.value.setMap(null);
if (record.value.car) {
record.value.car.stopMove();
map.value.remove(record.value.car);
}
};
const nowClusterType = ref<any>([]);
const cluster = ref<any>(null);
const MarkerInfo = ref();
//打开设备标记点窗体信息
const clusterCallback = (e: any) => {
nowClusterType.value = [];
//判断点击的是聚合点
if (e.clusterData.length > 0)
e.clusterData.forEach((item: any) => {
nowClusterType.value.push(item.info);
});
else nowClusterType.value.push(e.marker.info);
var infoWindow: any = new window.AMap.InfoWindow({
offset: new window.AMap.Pixel(0, nowClusterType.value.length > 1 ? -40 : -70),
content: MarkerInfo.value.$el,
anchor: 'bottom-center',
});
infoWindow.open(map.value, [e.lnglat.lng, e.lnglat.lat]);
if (fakeMarker.value) map.value.remove(fakeMarker.value);
if (nowClusterType.value.length === 1) {
fakeMarker.value = new window.AMap.Marker({
map: map.value,
position: [e.lnglat.lng, e.lnglat.lat],
offset: new window.AMap.Pixel(-16, -66),
icon: new window.AMap.Icon({
size: new window.AMap.Size(32, 74), // 图标尺寸
image: require('../../../assets/images/map/alarm-yellow.png'), // Icon的图像
imageOffset: new window.AMap.Pixel(0, 0), // 图像相对展示区域的偏移量,适于雪碧图等
imageSize: new window.AMap.Size(32, 74), // 根据所设置的大小拉伸或压缩图片
}),
});
}
//高德 InfoWindow解决2.0版本无法滚动问题
infoWindow.on('mouseover', () => map.value.setStatus({ zoomEnable: false }));
infoWindow.on('mouseout', () => {
map.value.setStatus({ zoomEnable: true });
});
infoWindow.on('mousewheel', (e: any) => {
const { originEvent } = e;
let doc: any = document.querySelector('.marker-content');
if (doc) doc.scrollTop -= originEvent.wheelDelta / 5;
});
};
const setCluster = () => {
map.value?.clearInfoWindow();
if (cluster.value) cluster.value.setMap(null);
if (!prop.sickList || prop.sickList.length === 0 || !window.AMap) return;
let markerList: any = [];
let allPath: any = [];
roadList.value.forEach((items: any) => {
allPath = allPath.concat(items._opts.path);
});
prop.sickList.forEach((item: any) => {
/*let pointsList: any = []
roadList.value[0].$x.forEach((items: any)=>{
items.forEach((itemss: any)=>{
pointsList.push(turf.point(itemss))
})
})
let points: any = turf.featureCollection(pointsList);
var targetPoint = turf.point([
coordtransform.wgs84togcj02(item.lon, item.lat)[0],
coordtransform.wgs84togcj02(item.lon, item.lat)[1],
]);*/
// 计算path上距离pos最近的点
markerList.push({
info: item,
lnglat: window.AMap.GeometryUtil.closestOnLine(
coordtransform.wgs84togcj02(item.lon, item.lat),
allPath
),
});
});
var _renderMarker = function (context: any) {
let icon = new window.AMap.Icon({
size: new window.AMap.Size(32, 74), // 图标尺寸
image: require('../../../assets/images/map/alarm-blue.png'), // Icon的图像
imageOffset: new window.AMap.Pixel(0, 0), // 图像相对展示区域的偏移量,适于雪碧图等
imageSize: new window.AMap.Size(32, 74), // 根据所设置的大小拉伸或压缩图片
});
context.marker.setOffset(new window.AMap.Pixel(-16, -66));
context.marker.setIcon(icon);
context.marker.info = context.data[0].info;
};
var _renderClusterMarker = function (context: any) {
var factor = Math.pow(context.count / prop.sickList.length, 1 / 18);
var div = document.createElement('div');
var Hue = 180 - factor * 180;
var bgColor = 'hsla(' + Hue + ',100%,40%,0.7)';
var fontColor = 'hsla(' + Hue + ',100%,90%,1)';
var borderColor = 'hsla(' + Hue + ',100%,40%,1)';
var shadowColor = 'hsla(' + Hue + ',100%,90%,1)';
div.style.backgroundColor = bgColor;
var size = Math.round(30 + Math.pow(context.count / prop.sickList.length, 1 / 5) * 20);
div.style.width = div.style.height = size + 'px';
div.style.border = 'solid 1px ' + borderColor;
div.style.borderRadius = size / 2 + 'px';
div.style.boxShadow = '0 0 5px ' + shadowColor;
div.innerHTML = context.count;
div.style.lineHeight = size + 'px';
div.style.color = fontColor;
div.style.fontSize = '14px';
div.style.textAlign = 'center';
context.marker.setOffset(new window.AMap.Pixel(-size / 2, -size / 2));
context.marker.setContent(div);
};
cluster.value = new window.AMap.MarkerCluster(map.value, markerList, {
gridSize: 80,
averageCenter: false,
renderClusterMarker: _renderClusterMarker, // 自定义聚合点样式
renderMarker: _renderMarker, // 自定义非聚合点样式
});
cluster.value.on('click', clusterCallback);
//map.value.setFitView();
};
watch(
() => prop.sickList,
() => {
setCluster();
}
);
watch(
() => prop.sickId,
() => {
if (cluster.value && cluster.value.it.length > 0) {
map.value.clearInfoWindow();
if (fakeMarker.value) map.value.remove(fakeMarker.value);
nowClusterType.value = [];
cluster.value.it.forEach((item: any) => {
if (item.info.id === prop.sickId) {
nowClusterType.value.push(item.info);
map.value.setCenter([item.lnglat.lng, item.lnglat.lat + 0.0002], true);
map.value.setZoom(20, true);
var infoWindow: any = new window.AMap.InfoWindow({
offset: new window.AMap.Pixel(0, -70),
content: MarkerInfo.value.$el,
anchor: 'bottom-center',
});
setTimeout(() => {
infoWindow.open(map.value, [item.lnglat.lng, item.lnglat.lat]);
fakeMarker.value = new window.AMap.Marker({
map: map.value,
position: [item.lnglat.lng, item.lnglat.lat],
offset: new window.AMap.Pixel(-16, -66),
icon: new window.AMap.Icon({
size: new window.AMap.Size(32, 74), // 图标尺寸
image: require('../../../assets/images/map/alarm-yellow.png'), // Icon的图像
imageOffset: new window.AMap.Pixel(0, 0), // 图像相对展示区域的偏移量,适于雪碧图等
imageSize: new window.AMap.Size(32, 74), // 根据所设置的大小拉伸或压缩图片
}),
});
}, 200);
}
});
}
}
);
watch(
() => prop.roadRecord,
() => {
let path: any = [];
removeRecord(false);
if (prop.roadRecord.length === 0) return;
prop.roadRecord.forEach((items: any) => {
path.push([
coordtransform.wgs84togcj02(items.longitude, items.latitude)[0],
coordtransform.wgs84togcj02(items.longitude, items.latitude)[1],
]);
});
record.value.line = new window.AMap.Polyline({
path: path,
strokeColor: '#00F9FF',
strokeOpacity: 1,
strokeWeight: 3,
strokeStyle: 'solid',
});
record.value.car = new window.AMap.Marker({
map: map.value,
position: path[0],
offset: new window.AMap.Pixel(-14, -25),
icon: new window.AMap.Icon({
size: new window.AMap.Size(28, 50), // 图标尺寸
image: require('../../../assets/images/map/car.png'), // Icon的图像
imageOffset: new window.AMap.Pixel(0, 0), // 图像相对展示区域的偏移量,适于雪碧图等
imageSize: new window.AMap.Size(28, 50), // 根据所设置的大小拉伸或压缩图片
}),
});
record.value.start = new window.AMap.Marker({
map: map.value,
position: path[0],
offset: new window.AMap.Pixel(-16, -55),
icon: new window.AMap.Icon({
size: new window.AMap.Size(32, 55), // 图标尺寸
image: require('../../../assets/images/map/start.png'), // Icon的图像
imageOffset: new window.AMap.Pixel(0, 0), // 图像相对展示区域的偏移量,适于雪碧图等
imageSize: new window.AMap.Size(32, 55), // 根据所设置的大小拉伸或压缩图片
}),
});
record.value.end = new window.AMap.Marker({
map: map.value,
position: path[path.length - 1],
offset: new window.AMap.Pixel(-16, -55),
icon: new window.AMap.Icon({
size: new window.AMap.Size(32, 55), // 图标尺寸
image: require('../../../assets/images/map/end.png'), // Icon的图像
imageOffset: new window.AMap.Pixel(0, 0), // 图像相对展示区域的偏移量,适于雪碧图等
imageSize: new window.AMap.Size(32, 55), // 根据所设置的大小拉伸或压缩图片
}),
});
record.value.start.dom.classList.add('marker-bottom');
record.value.end.dom.classList.add('marker-bottom');
record.value.car.moveAlong(path, {
duration: 100, //可根据实际采集时间间隔设置
autoRotation: true,
});
map.value.add(record.value.line);
map.value.setFitView(record.value.line);
},
{ deep: true }
);
return {
nowClusterType,
MarkerInfo,
};
},
});
</script>
地图样式
<style scoped lang="less">
#map-container {
width: 100%;
height: 100%;
background-color: black;
:deep(.amap-info-content) {
padding: 0;
background-color: transparent;
box-shadow: none;
}
:deep(.amap-info-sharp) {
border-top: 0 solid transparent;
bottom: 20px;
}
:deep(.amap-layer) {
background-color: black;
}
:deep(.amap-marker) {
&.marker-bottom {
&::after {
content: '';
position: absolute;
bottom: -9px;
left: 7px;
height: 18px;
width: 18px;
border-radius: 50%;
background: linear-gradient(180deg, #99f4f6, #00f9ff);
transform: rotateX(30deg);
}
}
}
}
</style>
地图加载方法
export function loadAMap() {
return new Promise((resolve, reject) => {
AMapLoader.load({
key: '', // 申请好的Web端开发者Key,首次调用 load 时必填
version: '2.0', // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
plugins: [
'AMap.ControlBar',
'AMap.LineSearch',
'AMap.StationSearch',
'AMap.PolylineEditor',
'AMap.PolygonEditor',
'AMap.CircleEditor',
'AMap.AutoComplete',
'AMap.DistrictSearch',
'AMap.PlaceSearch',
'AMap.MouseTool',
'AMap.moveAnimation',
'AMap.MarkerCluster',
], // 需要使用的的插件列表,如比例尺'AMap.Scale'等
})
.then((AMap: any) => {
window.AMap = AMap;
resolve(window.AMap);
})
.catch((e: any) => {
console.log(e);
reject();
});
});
}
车辆移动
let position: number[] = [
coordtransform.wgs84togcj02(
position_3d.lon,
position_3d.lat
)[0],
coordtransform.wgs84togcj02(
position_3d.lon,
position_3d.lat
)[1],
];
const currentPosition = [
obuMarker.value.getPosition().lng,
obuMarker.value.getPosition().lat,
];
const currentDis = window.AMap.GeometryUtil.distance(currentPosition, position);
const currentMileage = 5;
if (currentDis >= currentMileage) {
window.AMap.plugin(['AMap.MoveAnimation'], () => {
obuMarker.value.moveTo(position, {
duration: 1000,
autoRotation: true,
});
});
}
展示