说明一下用的不是常见的地图,内部的,特别会吹,被市面上的地图吊起来打,最坑的是那个糟糕的地图文档,连基本的参数他们有时候都不给全,还得加微信,跟他聊,而且移动端没有轨迹相关的api就把挺恶心的,后来我还问有没有更新计划,被明确回答没有,就仗着自己没竞争对手,这破地图还不淘汰,toG的,吐了。。。
轨迹地图,就只能自己写了。其实也简单,但是当时心态被那个破地图的对接人员影响了。 设计思路: 1.定位找到开始点 2.点击开始,向后端去发送请求标识起点,根据实际情况写一个定时器,每隔xx秒连续定位,请求后端标识中间点,在地图上连线(做异常值筛选等),最后请求后端在最后一个点标识终点 3.结束旅程后,拿到整个点位数组,在图上根据实际情况,设计绘制速率,还设计跟随最后一个点移动地图
开始函数
检测地图定位权限,开始后除非自己熄屏,就一直保持亮屏
const handleStart = async () => {
if (!navigator.geolocation) {
Toast.show(
'您的浏览器不支持地理定位功能,请使用支持地理定位的浏览器。'
);
return;
}
try {
const position = await new Promise((resolve, reject) => {
navigator.geolocation.getCurrentPosition(resolve, reject);
});
setIsTracking(true);
setStartTime(new Date());
setDistance(0);
try {
const wakeLock = await navigator.wakeLock.request('screen');
setWakeLock(wakeLock);
wakeLock.addEventListener('release', () => {
//xxxxx
});
} catch (err) {
console.error(`${err.name}, ${err.message}`);
}
startPatrol({
id: taskId,
lat: startPoint[1],
log: startPoint[0]
});
const currentStartTime = moment().format('YYYY-MM-DD HH:mm:ss');
setStartTime(currentStartTime);
document.querySelector('.start-time h2').textContent =
currentStartTime || '00:00:00';
const interval = setInterval(() => {
startLocationTracking();
}, xxxx);//根据具体情况
setTrackingInterval(interval);
} catch (error) {
Toast.show('请打开手机定位功能,以便开始巡检任务。');
}
};
定位(初次)
进入页面后,进行初次,获取到地图的高宽,对其进行适配。
const getLocation = () => {
initMap().then(initMapRes => {
if (initMapRes.err_msg == 'ext_SGMap_Init_V2:ok') {
setMapExist(true);
let heightSize = document.getElementById('map').clientHeight;
let widthSize = document.getElementById('map').clientWidth;
let u = navigator.userAgent;
let isAndroid =
u.indexOf('Android') > -1 || u.indexOf('Linux') > -1;
let isIOS = !!u.match(/(i[^;]+;( U;)? CPU.+Mac OS X/);
if (isAndroid) {
heightSize = heightSize*window.devicePixelRatio;
widthSize = widthSize * window.devicePixelRatio;
}
startLocation()
.then(startLocation => {
if (startLocation.data.coord.length > 0) {
stopLocation();
openMap({
headerSize: xx,
rightSize: 0,
heightSize: heightSize,
widthSize: widthSize
})
.then(res => {
setMapcenter(15, startLocation.data.coord);
addSource(startLocation.data.coord);
addLayer('sourceId');
window.sessionStorage.setItem(
'startMsg',
startLocation.data.coord
);
setLineCoordinates(prev => [
...prev,
startLocation.data.coord
]);
addLineSource(lineCoordinates, 12);
setStartPoint(startLocation.data.coord);
})
.catch(e => {
console.error('initMap', e);
});
}
})
.catch(e => {
console.error('startLocation', e);
});
}
});
};
开始定位
首先先停止上一个定位,开始正式,获取线相关点的数据,进行相关筛选,更新数据源,更新线数据源,在地图上进行绘制,不停地更新终点坐标。
const startLocationTracking = () => {
stopLocation();
startLocation().then(location => {
if (location.data.coord.length > 0) {
const newCoord = location.data.coord;
setLineCoordinates(prevCoords => {
const updatedCoords = [...prevCoords, newCoord];
// 确保有至少两个点
if (updatedCoords.length > 1) {
const lastCoord =
updatedCoords[updatedCoords.length - 2];
// 检查点是否不同
if (
lastCoord[0] === newCoord[0] &&
lastCoord[1] === newCoord[1]
) {
console.warn(
'新的坐标点与之前的坐标点相同,忽略本次更新'
);
return prevCoords; // 返回上一次的坐标,不进行更新
}
const distanceBetweenPoints = distanceFromLocation({
from: lastCoord,
to: newCoord
});
// 检查距离是否超过1000公里
if (distanceBetweenPoints > 1000) {
console.warn(
'距离超过1000公里,定位异常,忽略本次更新'
);
return prevCoords; // 返回上一次的坐标,不进行更新
}
setDistance(
prevDistance => prevDistance + distanceBetweenPoints
);
uploadPatrol({
id: taskId,
lat: newCoord[1],
log: newCoord[0],
mileage: distanceBetweenPoints
});
}
updateSource('source-line', {
type: 'FeatureCollection',
features: [
{
type: 'Feature',
geometry: {
type: 'LineString',
coordinates: updatedCoords
}
}
]
});
setEndPoint(newCoord);
return updatedCoords;
});
}
});
};
停止定位
停止定位相关api,停止相关数据,进行清空处理。还有销毁地图在另一个函数
const handleStop = () => {
if (endPoint) {
endPatrol({
id: taskId,
lat: endPoint[1],
log: endPoint[0],
mileage: distance
});
}
const endTime = moment().format('YYYY-MM-DD HH:mm:ss');
if (trackingInterval) {
clearInterval(trackingInterval);
setTrackingInterval(null);
}
document.querySelector('.end-time h2').textContent = endTime;
stopLocation();
setLineCoordinates([]);
setStartPoint(null);
setEndPoint(null);
setIsTracking(false);
setDistance(0);
};
开始轨迹回放
因为破地图底层是用mapbox做的,数据格式GeoJSON 数据
setInterval()方法可按照指定的周期(以毫秒计)来调用函数或计算表达式。这个多少秒纯看产品经理想法。然后更新起点和终点,去更细线数据源,在地图上绘制你想要地效果,线你也可以设置得好看一些,具体样式参考mapbox那些些参数
startPeriodicDrawing = points => {
this.currentPointIndex = 0;
this.allLines = []; // 保存所有线段
if (this.intervalId) {
clearInterval(this.intervalId);
}
this.intervalId = setInterval(() => {
if (this.currentPointIndex < points.length - 1) {
this.drawLineSegment(
points[this.currentPointIndex],
points[this.currentPointIndex + 1]
);
this.currentPointIndex++;
} else {
clearInterval(this.intervalId);
this.intervalId = null;
}
}, 500);
};
drawLineSegment = (startPoint, endPoint) => {
const line = [startPoint, endPoint];
this.allLines.push(line);
this.addOrUpdateLineSource(this.allLines.flat(), 3);
};
addOrUpdateLineSource = (coordinates, lineWidth) => {
ISGMapWX.Map.updateSource('source-line', {
type: 'FeatureCollection',
features: [
{
type: 'Feature',
geometry: {
type: 'LineString',
coordinates: coordinates
}
}
]
});
};
class MapStore2 {
constructor() {
makeAutoObservable(this);
}
initMap = () => {
//xxxxx
};
// 计算两点间距
distanceFromLocation = data => {
var options = { units: 'miles' };
var distance = ISGMapWX.Utility.distance(data.from, data.to, options);
return distance;
};
openMap = obj => {
return ISGMapWX.Map.initMap({
// 地图显示位置配置
position: {
right: obj.rightSize,
top: obj.headerSize,
width: obj.widthSize,
height: obj.heightSize
}
})
.then(res => {
return res;
})
.catch(e => {
console.error('initMapinitMap,地图启动失败', e);
});
};
onceParams = () => {
console.log('单次定位!启动!');
const locationOptions = {
interval: 5,
// Android
mode: 2,
gpsFirst: true,
gpsFirstTimeOut: 5,
// ios
distanceFilter: -1,
desiredAccuracy: -1
};
console.log('locationOptions', locationOptions);
ISGMapWX.Location.config(locationOptions);
const onceParams = {
accuracyPrioritise: true, // 精度优先
accuracyFilter: 5,
locationWithReGeocode: true, // 是否获取逆地理编码信息
reGeocodeTimeout: 20 // 地理编码请求超时时间
};
console.log('onceParams', onceParams);
ISGMapWX.Location.onceLocation(onceParams)
.then(res => {
console.log('单次定位开启成功', res);
})
.catch(e => {
console.error('单次定位开启失败', e);
});
};
startLocation = () => {
ISGMapWX.Location.startLocation({ locationWithReGeocode: true });
return new Promise((resolve, reject) => {
ISGMapWX.Location.on('updatingLocation', res => {
console.log(res, '持续定位');
resolve(res);
});
});
};
stopLocation = () => {
ISGMapWX.Location.stopLocation();
};
addSource = Point => {
console.log('点精度', Point);
ISGMapWX.Map.addSource('sourceId', {
type: 'geojson',
data: {
type: 'FeatureCollection',
features: [
{
type: 'Feature',
geometry: {
type: 'Point',
coordinates: Point
},
properties: {
name: '圆点'
}
}
]
}
}).then(function (res) {
console.log('res', res);
});
};
//移除数据源
removeSource = sourceId => {
ISGMap.Map.removeSource('sourceId', function (res) {
console.log(res);
});
};
//移除层级
removeLayer = layerId => {
ISGMapWX.Map.removeLayer('layerId', function (res) {
console.log(res);
});
};
addLayer = sourceId => {
return ISGMapWX.Map.addLayer({
id: 'layerId',
type: 'circle',
source: sourceId,
paint: {
'circle-radius': 3,
'circle-color': 'white',
'circle-stroke-color': 'red',
'circle-stroke-width': 5,
'circle-opacity': 0.5
}
}).then(function (res) {
console.log('addLayer', res);
});
};
setMapcenter = (zoom, center) => {
return ISGMapWX.Map.setMap({
zoom: zoom,
center: center
})
.then(function (res) {
console.log('setMap', res);
return res;
})
.catch(e => {
console.error('setMap', e);
});
};
closedestoryMap = () => {
return ISGMapWX.Map.destroyMap().then(res => {
return res;
});
};
drawLine = (line, callback) => {
ISGMapWX.UI.drawLine({ line: line })
.then(function (res) {
console.log('绘制线成功', res);
if (callback) callback(res);
})
.catch(function (e) {
console.error('绘制线失败', e);
});
};
// 添加线数据源
addLineSource = (coordinates, lineWidth) => {
console.log(coordinates, 'coordinates');
ISGMapWX.Map.addSource('source-line', {
type: 'geojson',
data: {
type: 'FeatureCollection',
features: [
{
type: 'Feature',
geometry: {
type: 'LineString',
coordinates: coordinates
}
}
]
}
})
.then(function (res) {
console.log('增加线资源', res);
})
.catch(function (e) {
console.error(e, 'bug1');
});
this.addLineLayer('source-line', lineWidth);
};
addLineAllSource = (coordinates, lineWidth) => {
console.log(coordinates, 'coordinates');
ISGMapWX.Map.addSource('source-line', {
type: 'geojson',
data: {
type: 'FeatureCollection',
features: [
{
type: 'Feature',
geometry: {
type: 'LineString',
coordinates: coordinates
}
}
]
}
})
.then(function (res) {
console.log('增加线资源', res);
})
.catch(function (e) {
console.error(e, 'bug1');
});
this.addLineLayer('source-line', lineWidth);
};
// 抽离出的添加线图层方法
addLineLayer = (sourceId, lineWidth) => {
ISGMapWX.Map.addLayer({
id: 'layer-line',
type: 'line',
source: sourceId,
layout: {
'line-cap': 'round',
'line-join': 'round'
},
paint: {
'line-color': 'red',
'line-width': lineWidth,
'line-blur': 2,
'line-opacity': 0.8
}
})
.then(function (res) {
console.log('增加线图资源', res);
})
.catch(function (e) {
console.error(e, 'bug2');
});
};
// 封装移除线数据源
removeLineSource = sourceId => {
ISGMapWX.Map.removeSource(sourceId)
.then(res => {
console.log('移除线数据源成功', res);
})
.catch(e => {
console.error('移除线数据源失败', e);
});
};
// 更新数据源
updateSource = (id, data, callback) => {
if (callback) {
ISGMapWX.Map.updateSource(id, data, callback);
} else {
return ISGMapWX.Map.updateSource(id, data)
.then(res => {
console.log('更新数据源成功', res);
})
.catch(e => {
console.error('更新数据源失败', e);
});
}
};
// 封装路线规划和绘制路线的方法
calculateAndDrawRoute = (
startPoint,
endPoint,
wayPoints,
mileage,
linesPadding
) => {
//小数对比,如果小于2,type为1
let routeParams;
if (mileage < 2) {
routeParams = {
startPoint: { lngLat: startPoint },
endPoint: { lngLat: endPoint },
type: 1,
drawMarkers: true,
drawUnselectedLines: false,
wayPoints: wayPoints.map(point => ({ lngLat: point }))
};
} else {
routeParams = {
startPoint: { lngLat: startPoint },
endPoint: { lngLat: endPoint },
type: 0,
drawMarkers: true,
drawUnselectedLines: false,
wayPoints: wayPoints.map(point => ({ lngLat: point }))
};
}
return ISGMapWX.Navi.calculateRoute(routeParams)
.then(routeRes => {
console.log('路线规划成功', routeRes);
return ISGMapWX.Navi.drawRoutes({ drawMarkers: true })
.then(drawRes => {
console.log('绘制路线成功', drawRes);
// this.simulateNavigation();
return drawRes;
})
.catch(drawErr => {
console.error('绘制路线失败', drawErr);
});
})
.catch(routeErr => {
console.error('路线规划失败', routeErr);
});
};
//模拟导航
simulateNavigation = () => {
ISGMapWX.Navi.startNavi({
isEmulatorNavi: true //是否开启模拟导航,默认为false。false实时导航,true模拟导航
}).then(function (res) {
console.log(res);
this.setZoom(10);
});
};
//结束导航
endNavigation = () => {
ISGMapWX.Navi.stopNavi()
.then(function (res) {
console.log(res, '结束导航');
this.setZoom(10);
})
.catch(function (e) {
console.log(e, '结束导航失败');
});
};
//清除路线
clearRoute = () => {
ISGMapWX.Navi.clearRoutes()
.then(function (res) {
console.log(res, '清除路线');
})
.catch(function (e) {
console.log(e);
});
};
//
startPeriodicDrawing = points => {
this.currentPointIndex = 0;
this.allLines = []; // 保存所有线段
if (this.intervalId) {
clearInterval(this.intervalId);
}
this.intervalId = setInterval(() => {
if (this.currentPointIndex < points.length - 1) {
this.drawLineSegment(
points[this.currentPointIndex],
points[this.currentPointIndex + 1]
);
console.log(
points[this.currentPointIndex],
points[this.currentPointIndex + 1],
this.currentPointIndex,
'points'
);
this.currentPointIndex++;
} else {
clearInterval(this.intervalId);
this.intervalId = null;
}
}, 500);
};
drawLineSegment = (startPoint, endPoint) => {
// console.log(startPoint, endPoint, 'sssssssssss');
// this.setMapcenter(15, startPoint);
const line = [startPoint, endPoint];
this.allLines.push(line);
this.addOrUpdateLineSource(this.allLines.flat(), 3);
};
addOrUpdateLineSource = (coordinates, lineWidth) => {
// If source exists, update its data
ISGMapWX.Map.updateSource('source-line', {
type: 'FeatureCollection',
features: [
{
type: 'Feature',
geometry: {
type: 'LineString',
coordinates: coordinates
}
}
]
});
};
stopPeriodicDrawing = () => {
if (this.intervalId) {
clearInterval(this.intervalId);
this.intervalId = null;
}
};
setZoom = zoom => {
ISGMapWX.Map.setZoom(zoom)
.then(function (res) {
console.log(res);
})
.catch(function (e) {
console.log(e);
});
};
}
其中一个破烂地图的使用,toG的项目地图文档是真的抽象,更多优化当然产品经理提了,在后面,不过他没提线样式和绘制地图速率和缓和度。这个地图我又没相关的代码,就这样了