摘要:有巡检业务或规划路线的场景,这个功能还是比较实用的;目前高德有类似的功能,天地图目前还没有;手动封装一个,效果还行;
效果图
天地图驾车规划接口
封装方法
// 开始marker图片_换成自己
import startPng from '@/assets/image/map/start.png';
// 结束marker图片_换成自己
import endPng from '@/assets/image/map/end.png';
// 途经点marker图片_换成自己
import midPng from '@/assets/image/map/mid.png';
// 弹窗提示_换成自己
import {AzMessages} from '@/views/Utils/index';
import { v4 as uuidv4 } from 'uuid';
const T = (window as any).T;
class DragRoute {
map: any;
startLngLat: any;
endLngLat: any;
midList: any;
line: any;
curInfoWindow: any;
startEndMarker: any;
eventListenerMap: any;
constructor(map: any, line: any) {
// 天地图
this.map = map;
// 开始坐标
this.startLngLat = {};
// 结束坐标
this.endLngLat = {};
// 线
this.line = line;
// 途径点
this.midList = [];
const curLngLats = this.line.getLngLats();
this.startLngLat = curLngLats[0];
this.endLngLat = curLngLats[curLngLats.length - 1];
// 当前信息窗口
this.curInfoWindow = new T.InfoWindow();
// 开始结束标记点_后续销毁用
this.startEndMarker = [];
// 事件监听Map_后续销毁用
this.eventListenerMap = new Map();
this.init();
}
init() {
this.createStartMarker();
this.createEndMarker();
this.createLine();
}
// 创建开始
createStartMarker() {
if (!this.map) { return; }
var marker = new T.Marker(this.startLngLat, {
icon: new T.Icon({
iconUrl: startPng,
iconSize: new T.Point(25, 30),
iconAnchor: new T.Point(14, 30)
}),
draggable: true,
});
marker.uuid = uuidv4();
this.map.addOverLay(marker);
const dragendCallback = () => {
this.startLngLat = marker.getLngLat();
this.change();
}
marker.addEventListener('dragend', dragendCallback);
this.startEndMarker.push(marker);
// 把事件存起来_后续销毁用
this.eventListenerMap.set(marker.uuid, {
marker, event:'dragend', eventListener: dragendCallback
});
}
// 创建结束
createEndMarker() {
if (!this.map) { return; }
var marker = new T.Marker(this.endLngLat, {
icon: new T.Icon({
iconUrl: endPng,
iconSize: new T.Point(25, 30),
iconAnchor: new T.Point(14, 30)
}),
draggable: true,
});
marker.uuid = uuidv4();
this.map.addOverLay(marker);
const dragendCallback = () => {
this.endLngLat = marker.getLngLat();
this.change();
}
marker.addEventListener('dragend', dragendCallback);
this.startEndMarker.push(marker);
// 把事件存起来_后续销毁用
this.eventListenerMap.set(marker.uuid, {
marker, event:'dragend', eventListener: dragendCallback
});
}
createLine() {
if (!this.map) { return; }
this.line.setLngLats([this.startLngLat, this.endLngLat]);
const mouseupCallback = (e: any) => {
this.createMidMarker(e.lnglat);
}
this.line.addEventListener('mouseup', mouseupCallback);
// 把事件存起来_后续销毁用
this.eventListenerMap.set('line', {
marker: this.line, event:'mouseup', eventListener: mouseupCallback
})
}
// 创建途径点
createMidMarker(lngLat: any) {
if (!this.map) { return; }
if (this.midList > 19) {
AzMessages('warning', '最多可添加20个途径点');
return;
}
var marker = new T.Marker(lngLat, {
icon: new T.Icon({
iconUrl: midPng,
iconSize: new T.Point(25, 30),
iconAnchor: new T.Point(14, 30)
}),
draggable: true,
});
marker.uuid = uuidv4();
this.map.addOverLay(marker);
this.midList.push(marker);
const dragendCallback = () => {
this.change();
this.openInfoWindow(marker);
}
const mousedownCallback = () => {
this.openInfoWindow(marker);
}
const dragstartCallback = () => {
marker.closeInfoWindow();
}
marker.addEventListener('dragend', dragendCallback);
marker.addEventListener('mousedown',mousedownCallback);
marker.addEventListener('dragstart', dragstartCallback);
// 把事件存起来_后续销毁用
this.eventListenerMap.set(`${marker.uuid}Dragend`, {
marker, event:'dragend', eventListener: dragendCallback
});
this.eventListenerMap.set(`${marker.uuid}mousedown`, {
marker, event:'mousedown', eventListener: mousedownCallback
});
this.eventListenerMap.set(`${marker.uuid}dragstart`, {
marker, event:'dragstart', eventListener: dragstartCallback
});
}
change() {
// 有点改变了
const orig = `${this.startLngLat.lng},${this.startLngLat.lat}`;
const dest = `${this.endLngLat.lng},${this.endLngLat.lat}`;
let mid = [];
for (let i = 0; i < this.midList.length; i++) {
const item = this.midList[i].getLngLat();
mid.push(`${item.lng},${item.lat}`);
}
this.fetchLngLat({
orig,
dest,
mid: mid.join(';')
});
}
fetchLngLat(params: {orig: string, dest: string, mid: string}) {
return new Promise((resolve, reject) => {
const {orig, dest, mid} = params;
// 注意换成你的天地图密钥
const tk = '您的密钥';
const url = `https://api.tianditu.gov.cn/drive?postStr={"orig":"${orig}","dest":"${dest}","mid": "${mid}","style":"1"}&type=search&tk=${tk}`;
fetch(url)
.then(response => {
if (!response.ok || response.status !== 200) {
throw new Error(response.statusText);
}
return response.text(); // 获取响应的文本内容
})
.then(text => {
const parser = new DOMParser();
const xmlDoc = parser.parseFromString(text, "application/xml"); // 解析XML文本
// 查找<routelatlon>标签
const routeLatLonElement: any = xmlDoc.getElementsByTagName('routelatlon')[0];
if (routeLatLonElement) {
// 获取<routelatlon>标签下的文本,即经纬度数据
const latLonData = routeLatLonElement.textContent.trim();
const latLonArray = latLonData.split(';').map((item: string) => item.trim()).filter((item: any) => {
return item || false;
}); // 分割字符串并去除空白
this.updateLine(latLonArray);
resolve(latLonArray);
} else {
resolve([orig, dest]);
}
})
.catch(error => {
reject(error);
});
});
}
updateLine(lngLats: any) {
if (!this.map) { return; }
const points = lngLats.map((item: any) => {
const [lng, lat] = item.split(',');
return new T.LngLat(lng, lat);
});
this.line.setLngLats(points);
}
openInfoWindow(curMarker: any) {
if (!this.map) { return; }
const lngLat = curMarker.getLngLat();
const parentDiv = document.createElement('div');
const childDel = document.createElement('button');
childDel.innerText = '删除';
// parentDiv.appendChild(childUpper);
// parentDiv.appendChild(childDown);
parentDiv.appendChild(childDel);
this.curInfoWindow.setContent(parentDiv);
const delClickCallback = () => {
curMarker.closeInfoWindow()
this.map.removeOverLay(curMarker);
this.midList = this.midList.filter((item: any) => {
return item.uuid !== curMarker.uuid;
});
this.change();
}
childDel.addEventListener('click', delClickCallback);
// 把事件存起来_后续销毁用
this.eventListenerMap.set(`${curMarker.uuid}delClick`, {
marker: childDel, event:'click', eventListener: delClickCallback
});
curMarker.openInfoWindow(this.curInfoWindow, lngLat);
}
handleMidListSort(action: string, uuid: string) {
if (action === 'upper') {
const index = this.midList.findIndex((item: any) => item.uuid === uuid);
if (index > 0) { // 如果不是第一个元素
[this.midList[index], this.midList[index - 1]] = [this.midList[index - 1], this.midList[index]];
this.change();
}
} else if (action === 'down') {
const index = this.midList.findIndex((item: any) => item.uuid === uuid);
if (index < this.midList.length - 1) { // 如果不是最后一个元素
// 交换当前元素和后一个元素的位置
[this.midList[index], this.midList[index + 1]] = [this.midList[index + 1], this.midList[index]];
this.change();
}
}
}
end() {
if (!this.map) { return; }
for (let i = 0; i< this.startEndMarker.length; i++) {
this.map.removeOverLay(this.startEndMarker[i]);
}
for (let i = 0; i< this.midList.length; i++) {
this.map.removeOverLay(this.midList[i]);
this.midList[i].closeInfoWindow();
}
this.reset();
}
reset() {
this.map = null;
this.line = null;
this.midList = [];
this.curInfoWindow = null;
this.startEndMarker = [];
for (const [key, value] of this.eventListenerMap) {
value.marker.removeEventListener(value.event, value.eventListener);
}
this.eventListenerMap.clear();
}
}
export default DragRoute;
使用
注意:因为传入的是你创建的line; 引用内存地址是一样的;所以以你想获取最新的坐标直接掉api就可以;
line.getLngLats();
// 这里用的是React; 你可以换成你喜欢的vue
const dragRouteEditRef = useRef();
// 开始
const handleStartDragRouteEdit = () => {
// 传入你的地图和线
dragRouteEditRef.current = new DragRoute(map, line);
}
// 结束
const handleEndDragRouteEdit = () => {
dragRouteEditRef.current.end();
dragRouteEditRef.current = null;
};
map和line创建
//初始化地图对象
map = new T.Map("mapDiv");
//设置显示地图的中心点和级别
map.centerAndZoom(new T.LngLat(116.40969, 39.94940), zoom);
points = [];
points.push(new T.LngLat(116.41136, 39.97569));
points.push(new T.LngLat(116.411794, 39.9068));
points.push(new T.LngLat(116.32969, 39.92940));
points.push(new T.LngLat(116.385438, 39.90610));
//创建线对象
var line = new T.Polyline(points);
//向地图上添加线
map.addOverLay(line);