H5(其中一个破烂地图使用,地图轨迹)React+mobx

688 阅读6分钟

说明一下用的不是常见的地图,内部的,特别会吹,被市面上的地图吊起来打,最坑的是那个糟糕的地图文档,连基本的参数他们有时候都不给全,还得加微信,跟他聊,而且移动端没有轨迹相关的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的项目地图文档是真的抽象,更多优化当然产品经理提了,在后面,不过他没提线样式和绘制地图速率和缓和度。这个地图我又没相关的代码,就这样了