前言

官网指引,生成accesstoken,下载相关依赖请翻阅[https://blog.csdn.net/weixin_44402694/article/details/125414381?spm=1001.2014.3001.5501](https://blog.csdn.net/weixin_44402694/article/details/125414381?spm=1001.2014.3001.5501)
本文使用官网accesstoken,请自行生成私人token
实现mapbox的测距功能

效果

实现代码

步骤1、前置资源

<linkrel="stylesheet"href="http://resource.geointech.cn:8040/mapbox/mapbox-gl.css"
/>
<script src="http://resource.geointech.cn:8040/mapbox/mapbox-gl.js"></script>
// turfjs工具计算面积
<script src="http://resource.geointech.cn:8040/turf/turf.js"></script>
// 步骤2的html代码中引入了measure-distance.js,当前文件在步骤3
import { measureLineLength, closeMeasureLine } from './mesaure-distance.js'

步骤2、创建以下html

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta http-equiv="X-UA-Compatible" content="IE=edge" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>测距</title><linkrel="stylesheet"href="http://resource.geointech.cn:8040/mapbox/mapbox-gl.css"/><script src="http://resource.geointech.cn:8040/mapbox/mapbox-gl.js"></script><script src="http://resource.geointech.cn:8040/turf/turf.js"></script><style>* {padding: 0;margin: 0;}html,body,#map {width: 100%;height: 100%;position: relative;}.btns {position: absolute;top: 40px;right: 40px;}button {cursor: pointer;}</style></head><body><div id="map"></div><div class="btns"><button class="open-btn">开始测距</button><button class="close-btn">结束测距</button></div><script type="module">import { measureLineLength, closeMeasureLine } from './mesaure-distance.js'mapboxgl.accessToken = 'mytoken'let map = nullwindow.onload = function () {initCreateMapHandler()}// 初始化创建地图function initCreateMapHandler(params) {map = new mapboxgl.Map({container: 'map', // container idstyle: 'mapbox://styles/mapbox/streets-v11', // stylesheet locationcenter: [108, 35], // starting position [lng, lat]zoom: 2, // starting zoom})btnBindingEventHandler()}// 地图创建后按钮绑定事件function btnBindingEventHandler() {const openBtn = document.querySelector('.open-btn')const closeBtn = document.querySelector('.close-btn')openBtn.addEventListener('click', () => {measureLineLength(map)})closeBtn.addEventListener('click', () => {closeMeasureLine(map)})}</script></body>
</html>

步骤3: 创建measure-distance.js

const initData = {clickMeasurePointsFunction: null,mapClickFunction: null,mapMousemoveFunction: null,mapDblClickFunction: null,map: null,isMeasure: true,jsonPoint: { 'type': 'FeatureCollection', 'features': [] },jsonLine: { 'type': 'FeatureCollection', 'features': [] },points: []
}
let before = null
let after = null/*** 测距* @param mapObject*/
export function measureLineLength (mapObject) {const currentMapContainerId = mapObject._container.getAttribute('id')currentMapContainerId.indexOf('-after') !== -1 ? after = JSON.parse(JSON.stringify(initData)) : before = JSON.parse(JSON.stringify(initData))const resultData = getBeforeOrAfterDataByMapContainerIdHandler(mapObject)resultData.isMeasure = trueresultData.map = mapObjectresultData.map.doubleClickZoom.disable() // 禁止双击缩放resultData.map.getCanvas().style.cursor = 'default'clearMeasureLine(mapObject)resultData.jsonPoint = { 'type': 'FeatureCollection', 'features': [] }resultData.jsonLine = { 'type': 'FeatureCollection', 'features': [] }resultData.points = []const { mouseLabel, ele } = createMeasurLineLabelMarkerHandler(mapObject)createMeasureLinePLLHandler(resultData.jsonPoint, resultData.jsonLine, mapObject)let timer = nullfunction mapClickHandler (_e) {timer = setTimeout(() => {clearTimeout(timer)if (resultData.isMeasure) {const coords = [_e.lngLat.lng, _e.lngLat.lat]addMeasureLineRes(resultData.points, resultData.jsonPoint, coords, mapObject)addMeasureLinePoint(resultData.jsonPoint, resultData.jsonLine, coords, mapObject)resultData.points.push(coords)}}, 100)}resultData.mapClickFunction = mapClickHandlerresultData.map.off('click', mapClickHandler)resultData.map.on('click', mapClickHandler)function mousemoveHandler (_e) {if (resultData.isMeasure) {const coords = [_e.lngLat.lng, _e.lngLat.lat]if (resultData.jsonPoint.features.length > 0) {const prev = resultData.jsonPoint.features[resultData.jsonPoint.features.length - 1]const json = {type: 'Feature',geometry: {type: 'LineString',coordinates: [prev.geometry.coordinates, coords]}}resultData.map.getSource('measure-line-move').setData(json)let resultText = '起点'if (resultData.points.length !== 0) {const prev = resultData.jsonPoint.features[resultData.jsonPoint.features.length - 1]const prevCoordinates = prev ? prev.geometry.coordinates : ''const resultAngle = prevCoordinates ? getAngleHandle(prevCoordinates[0], prevCoordinates[1], coords[0], coords[1]).toFixed(1) + '°' : ''resultText = `<span>${getMeasureLineLength(resultData.points, coords)} / ${resultAngle}</span><span>单击确定地点,双击结束</span>`}ele.innerHTML = resultText} else {ele.style.display = 'block'ele.innerHTML = '点击地图开始测量'}mouseLabel.setLngLat(coords)}}resultData.mapMousemoveFunction = mousemoveHandlerresultData.map.off('mousemove', mousemoveHandler)resultData.map.on('mousemove', mousemoveHandler)function dblclickHandler (_e) {if (timer) {clearTimeout(timer)}if (resultData.isMeasure) {const coords = [_e.lngLat.lng, _e.lngLat.lat]addMeasureLinePoint(resultData.jsonPoint, resultData.jsonLine, coords, mapObject)resultData.isMeasure = falseresultData.map.getCanvas().style.cursor = ''mouseLabel.remove()// 当前已关闭点击测距,所以清除move的线段resultData.map.getSource('measure-line-move').setData({ 'type': 'FeatureCollection', 'features': [] })// 鼠标移入当前测距的点位,动态显示tips提示语resultData.map.on('mouseover', 'measure-line-points', (event) => {if (resultData.isMeasure) {return}resultData.map.getCanvas().style.cursor = 'pointer'const tipsCoords = [event.lngLat.lng, event.lngLat.lat]const tipsEle = document.createElement('div')tipsEle.setAttribute('class', 'measure-line-result delete-tips')tipsEle.innerHTML = '单击可删除此点'const tipsOption = {element: tipsEle,anchor: 'left',offset: [10, 20]}// eslint-disable-next-line no-undefnew mapboxgl.Marker(tipsOption).setLngLat(tipsCoords).addTo(resultData.map)})// 鼠标移出当前测距的点位,移除tips提示语resultData.map.on('mouseout', 'measure-line-points', () => {if (resultData.isMeasure) {resultData.map.getCanvas().style.cursor = 'default'} else {resultData.map.getCanvas().style.cursor = ''}const deleteTips = resultData.map._container.querySelector('.delete-tips')if (deleteTips) {deleteTips.remove()}})/*** 点击测距点位触发* @param event* */// eslint-disable-next-line no-undef,no-inner-declarationsfunction clickMeasurePointsHandler (event) {if (resultData.jsonPoint.features.length > 2) {const features = resultData.map.queryRenderedFeatures(event.point)if (features.length > 0) {const measureResult = resultData.map._container.querySelectorAll('.measure-line-result')if (measureResult && measureResult.length > 0) {Array.from(measureResult).forEach((m) => {m.remove()})}const { index } = features[0].propertiesresultData.jsonPoint.features = resultData.jsonPoint.features.filter(feature => feature.properties.index !== index)resultData.jsonLine.features = []const featuresArr = [...resultData.jsonPoint.features]const resultPoints = []featuresArr.forEach((feature, index) => {const nextIndex = index + 1if (featuresArr[nextIndex]) {const current = featuresArr[index]const next = featuresArr[nextIndex]resultData.jsonLine.features.push({type: 'Feature',geometry: {type: 'LineString',coordinates: [current.geometry.coordinates, next.geometry.coordinates]}})}resultPoints.push(feature.geometry.coordinates)const ele = document.createElement('div')ele.setAttribute('class', 'measure-line-result')if (index === 0) {ele.innerHTML = '起点'} else {const prevIndex = index - 1const prevCoordinates = featuresArr[prevIndex].geometry.coordinatesconst currentCoordinates = feature.geometry.coordinatesconst resultAngle = prevCoordinates ? getAngleHandle(prevCoordinates[0], prevCoordinates[1], currentCoordinates[0], currentCoordinates[1]).toFixed(1) + '°' : ''ele.innerHTML = `${getMetersHandler(resultPoints, currentCoordinates)} / ${resultAngle}`}if (nextIndex === featuresArr.length) { // 终点需要再加一个marker// 添加关闭按钮createCloseMarkerHandler(clickMeasurePointsHandler, feature.geometry.coordinates, mapObject)}const left = window.document.documentElement.clientWidth > 7000 ? 20 : 8const option = {element: ele,anchor: 'left',offset: [left, 0]}// eslint-disable-next-line no-undefnew mapboxgl.Marker(option).setLngLat(feature.geometry.coordinates).addTo(resultData.map)})resultData.map.getSource('measure-line-points').setData(resultData.jsonPoint)resultData.map.getSource('measure-line').setData(resultData.jsonLine)resultData.map.getSource('measure-line-move').setData({ 'type': 'FeatureCollection', 'features': [] })}} else {closeMeasureLine(mapObject)}}resultData.clickMeasurePointsFunction = clickMeasurePointsHandlerresultData.map.off('click', 'measure-line-points', clickMeasurePointsHandler)resultData.map.on('click', 'measure-line-points', clickMeasurePointsHandler)// 添加关闭按钮createCloseMarkerHandler(clickMeasurePointsHandler, coords, mapObject)}}resultData.mapDblClickFunction = dblclickHandlerresultData.map.off('dblclick', dblclickHandler)resultData.map.on('dblclick', dblclickHandler)
}/*** 清除测距相关*/
function clearMeasureLine (mapObject) {const resultData = getBeforeOrAfterDataByMapContainerIdHandler(mapObject)const measureResult = resultData.map._container.querySelectorAll('.measure-line-result')if (measureResult && measureResult.length > 0) {Array.from(measureResult).forEach((m) => {m.remove()})}const source = resultData.map.getSource('measure-line-points')const json = {'type': 'FeatureCollection','features': []}if (source) {resultData.map.getSource('measure-line-points').setData(json)resultData.map.getSource('measure-line-move').setData(json)resultData.map.getSource('measure-line').setData(json)}
}/*** 创建label marker* @returns {*} 返回marker对象*/
function createMeasurLineLabelMarkerHandler (mapObject) {const resultData = getBeforeOrAfterDataByMapContainerIdHandler(mapObject)const ele = document.createElement('div')ele.style.display = 'none'ele.setAttribute('class', 'measure-line-result')const windowW = document.documentElement.clientWidthconst top = windowW > 7000 ? 120 : 44const left = window.document.documentElement.clientWidth > 7000 ? 20 : 8const option = {element: ele,anchor: 'left',offset: [left, top]}// eslint-disable-next-line no-undefconst mouseLabel = new mapboxgl.Marker(option).setLngLat([0, 0]).addTo(resultData.map)return {mouseLabel,ele}
}/*** 创建测距需要的layers,points、line、measure-line-move* @param jsonPoint* @param jsonLine* @param mapObject*/
function createMeasureLinePLLHandler (jsonPoint, jsonLine, mapObject) {const resultData = getBeforeOrAfterDataByMapContainerIdHandler(mapObject)const source = resultData.map.getSource('measure-line-points')if (source) {resultData.map.getSource('measure-line-points').setData(jsonPoint)resultData.map.getSource('measure-line-move').setData(jsonLine)resultData.map.getSource('measure-line').setData(jsonLine)} else {resultData.map.addSource('measure-line-points', {type: 'geojson',data: jsonPoint})resultData.map.addSource('measure-line', {type: 'geojson',data: jsonLine})resultData.map.addSource('measure-line-move', {type: 'geojson',data: jsonLine})const windowW = document.documentElement.clientWidthconst LW = windowW > 7000 ? 6 : 2const RW = windowW > 7000 ? 10.5 : 3.5const CW = windowW > 7000 ? 7.5 : 2.5resultData.map.addLayer({id: 'measure-line-move',type: 'line',source: 'measure-line-move',paint: {'line-color': '#0000ff','line-width': LW,'line-opacity': 0.5}})resultData.map.addLayer({id: 'measure-line',type: 'line',source: 'measure-line',paint: {'line-color': '#0000ff','line-width': LW,'line-opacity': 1}})resultData.map.addLayer({id: 'measure-line-points',type: 'circle',source: 'measure-line-points',paint: {'circle-color': '#ffffff','circle-radius': RW,'circle-stroke-width': CW,'circle-stroke-color': '#0000ff'}})}moveLayerHandler(resultData.map)
}/*** 将测距的三个图层移到最上层级*/
function moveLayerHandler (mapObject) {const { map } = getBeforeOrAfterDataByMapContainerIdHandler(mapObject)const { layers } = map.getStyle()const length = layers.lengthconst lastLayerId = map.getStyle().layers[length - 1].idif (lastLayerId !== 'measure-line-points') {map.moveLayer('measure-line-points', map.getStyle().layers[length - 1].id)map.moveLayer('measure-line', 'measure-line-points')map.moveLayer('measure-line-move', 'measure-line')}
}/*** 添加点位layer* @param jsonPoint* @param jsonLine* @param coords* @param mapObject*/
function addMeasureLinePoint (jsonPoint, jsonLine, coords, mapObject) {const { map } = getBeforeOrAfterDataByMapContainerIdHandler(mapObject)if (jsonPoint.features.length > 0) {const prev = jsonPoint.features[jsonPoint.features.length - 1]jsonLine.features.push({type: 'Feature',geometry: {type: 'LineString',coordinates: [prev.geometry.coordinates, coords]}})map.getSource('measure-line').setData(jsonLine)}jsonPoint.features.push({type: 'Feature',geometry: {type: 'Point',coordinates: coords},properties: {index: jsonPoint.features.length}})// 重新整理point点位,若是重复点位则不添加const data = [...jsonPoint.features]const result = []data.forEach((feature, index) => {if (data[index - 1]) {if (data[index - 1].geometry.coordinates[0] !== feature.geometry.coordinates[0] || data[index - 1].geometry.coordinates[1] !== feature.geometry.coordinates[1]) {result.push(feature)}} else {result.push(feature)}})jsonPoint.features = [...result]map.getSource('measure-line-points').setData(jsonPoint)
}/*** 获取两坐标点之间的距离* @param points* @param coords* @returns {string}*/
function getMeasureLineLength (points, coords) {const _points = points.concat([coords])// eslint-disable-next-line no-undefconst line = turf.lineString(_points)// eslint-disable-next-line no-undeflet len = turf.length(line)if (len < 1) {len = Math.round(len * 1000) + '米'} else {len = len.toFixed(2) + '公里'}return len
}/*** 点击地图添加点位触发给当前点位绑定测距结果的labelMarker* @param points* @param jsonPoint* @param coords* @param mapObject* */
function addMeasureLineRes (points, jsonPoint, coords, mapObject) {const { map } = getBeforeOrAfterDataByMapContainerIdHandler(mapObject)const ele = document.createElement('div')ele.setAttribute('class', 'measure-line-result')const left = window.document.documentElement.clientWidth > 7000 ? 20 : 8const option = {element: ele,anchor: 'left',offset: [left, 0]}let resultText = '起点'if (points.length !== 0) {const prev = jsonPoint.features[jsonPoint.features.length - 1]const prevCoordinates = prev ? prev.geometry.coordinates : ''const resultAngle = prevCoordinates ? getAngleHandle(prevCoordinates[0], prevCoordinates[1], coords[0], coords[1]).toFixed(1) + '°' : ''resultText = `${getMeasureLineLength(points, coords)} / ${resultAngle}`}ele.innerHTML = resultText// eslint-disable-next-line no-undefnew mapboxgl.Marker(option).setLngLat(coords).addTo(map)
}/*** 点击测距点位重新触发计算两点位之间的距离* @param resultPoints* @param coords* @returns {string}*/
function getMetersHandler (resultPoints, coords) {if (resultPoints.length > 1) {const _points = [...resultPoints]// eslint-disable-next-line no-undefconst line = turf.lineString(_points)// eslint-disable-next-line no-undeflet len = turf.length(line)if (len < 1) {len = Math.round(len * 1000) + '米'} else {len = len.toFixed(2) + '公里'}return len}
}/*** 获取两点位之间的角度* @param lng1* @param lat1* @param lng2* @param lat2* @returns {number}*/
function getAngleHandle (lng1, lat1, lng2, lat2) {const a = (90 - lat2) * Math.PI / 180const b = (90 - lat1) * Math.PI / 180const AOC_BOC = (lng2 - lng1) * Math.PI / 180const cosc = Math.cos(a) * Math.cos(b) + Math.sin(a) * Math.sin(b) * Math.cos(AOC_BOC)const sinc = Math.sqrt(1 - cosc * cosc)const sinA = Math.sin(a) * Math.sin(AOC_BOC) / sincconst A = Math.asin(sinA) * 180 / Math.PIlet res = 0if (lng2 > lng1 && lat2 > lat1) {res = A} else if (lng2 > lng1 && lat2 < lat1) {res = 180 - A} else if (lng2 < lng1 && lat2 < lat1) {res = 180 - A} else if (lng2 < lng1 && lat2 > lat1) {res = 360 + A} else if (lng2 > lng1 && lat2 === lat1) {res = 90} else if (lng2 < lng1 && lat2 === lat1) {res = 270} else if (lng2 === lng1 && lat2 > lat1) {res = 0} else if (lng2 === lng1 && lat2 < lat1) {res = 180}return res
}/*** 创建关闭按钮* @param clickMeasurePointsHandler* @param coords* @param mapObject*/
function createCloseMarkerHandler (clickMeasurePointsHandler, coords, mapObject) {const resultData = getBeforeOrAfterDataByMapContainerIdHandler(mapObject)const ele = document.createElement('div')ele.setAttribute('class', 'measure-line-result close')const left = window.document.documentElement.clientWidth > 7000 ? 20 : 8const top = window.document.documentElement.clientWidth > 7000 ? -50 : -11const option = {element: ele,anchor: 'bottom-left',offset: [left, top]}ele.innerHTML = '×'// eslint-disable-next-line no-undefnew mapboxgl.Marker(option).setLngLat(coords).addTo(resultData.map)ele.onclick = function (__e) {__e.stopPropagation()closeMeasureLine(resultData.map)}
}/*** 关闭触发*/
export function closeMeasureLine (mapObject) {if (!mapObject) returnconst resultData = getBeforeOrAfterDataByMapContainerIdHandler(mapObject)if (!resultData) returnresultData.map.doubleClickZoom.enable()clearMeasureLine(resultData.map)resultData.map.off('click', resultData.mapClickFunction)resultData.map.off('mousemove', resultData.mapMousemoveFunction)resultData.map.off('dblclick', resultData.mapDblClickFunction)resultData.map.off('click', 'measure-line-points', resultData.clickMeasurePointsFunction)resultData.isMeasure = falseresultData.map.getCanvas().style.cursor = ''resultData.map.getSource('measure-line-move').setData({ 'type': 'FeatureCollection', 'features': [] })resultData.jsonPoint = { 'type': 'FeatureCollection', 'features': [] }resultData.jsonLine = { 'type': 'FeatureCollection', 'features': [] }resultData.points = []
}// 根据地图对象的id来输出当前获取对应的全局变量数据
function getBeforeOrAfterDataByMapContainerIdHandler (mapObject) {if (!mapObject) returnconst currentMapContainerId = mapObject._container.getAttribute('id')return currentMapContainerId.indexOf('-after') !== -1 ? after : before
}

记录:供后续查阅使用

【MAPBOX基础功能】32、实现mapbox的测距功能相关推荐

  1. 多路测量实时同步工作原理_TOF测距功能的原理及使用方法

    摘要:该方法属于双向测距技术,利用数据信号在一对收发机之间往返的飞行时间来测量两点间的距离.将发射端发出数据信号和接收到接收端应答信号的时间间隔记为Tt,接收端收到发射端的数据信号和发出应答信号的时间 ...

  2. TOF测距功能的原理及使用方法

    一.飞行时间测距法TOF(time-of-flight)测距方法 该方法属于双向测距技术,利用数据信号在一对收发机之间往返的飞行时间来测量两点间的距离.将发射端发出数据信号和接收到接收端应答信号的时间 ...

  3. 【MAPBOX基础功能】05、底图切换 - mapbox切换高德、天地图、bingmap等底图

    前言 官网指引,生成accesstoken,下载相关依赖请翻阅[https://blog.csdn.net/weixin_44402694/article/details/125414381?spm= ...

  4. [mapbox] 基础

    文章目录 换图片 icon-image表达式 case match 换线的颜色 match 换点的颜色 match 设置地图中心 setCenter 图层显示 隐藏 visibility 图层不忽略短 ...

  5. QML QtLocation地图应用学习-2:实现测距功能

    1.实现思路 参照百度or高德地图的测距功能,主要由两种元素组成,标记点和连线. 其中连线很好解决,Qt 提供了 MapPolyline 类型,可以用来绘制折线,并且提供了增删的便捷函数: 对于标记点 ...

  6. ThreeJS 测距功能

    文章目录 选点绘线 绘制标签 1.使用 TextGeometry 创建标签文字 2. 使用 CSS2DObject 创建标签 动态绘制点.线和标签 绘制辅助线 撤销操作   测距功能,也就是选择两点, ...

  7. Mapbox浅析(快速入门Mapbox)

    1.是什么? Mapbox是一个可以免费创建并定制个性化地图的网站. 2.了解一些基本东西 常见的 mapbox.js和mapbox-gl.js的异同点? 相同点: 1.都是由Mapbox公司推出的免 ...

  8. (jQuery,SVG)使用jQuery和svg仿QQ地图测距功能(抛砖引玉)

    不久前看到了QQ地图的测距功能,觉得挺好玩的,就思考模仿一下.本来想通过canvas来画图,可惜对canvas不是很熟悉,就准备用svg了,其实我对svg也不是很熟,纯粹是学习. 代码只是简单的生成图 ...

  9. 高德地图JSAPI测距功能优化

    文章目录 前言 测距实现思路 使用测距插件 开启测距 关闭测距 前言 高德提供了一个距离测量插件可直接使用,但是没有完全满足需求.在测距过程中只会显示新增节点到起始点的总长度,而不会在鼠标移动过程中显 ...

最新文章

  1. (转)fastcgi简介
  2. 远距离蓝牙四驱小车方案
  3. ncnn:提取所有层特征值
  4. excel保存成matlab,matlab数据保存为excel文件
  5. 前端学习(1970)vue之电商管理系统电商系统之渲染添加参数的对话框
  6. Python基础小结
  7. linux 特定用户ssh,linux - 如何在登录后将SSH用户限制为一组预定义的命令?
  8. 剑指offer--面试题13
  9. Angularjs 中使用 layDate 日期控件
  10. 安装Visual studio 2013并进行单元测试
  11. 题目1140:八皇后
  12. [Linux] Ubuntu13.04 搭建OK6410-A开发板的开发环境
  13. 第2章——R的数据组织
  14. HTML5制作坦克大战游戏+Canvas绘制基础图形——学习笔记一
  15. 如何合理的拆分微服务
  16. Python项目:外星人入侵(汇总)
  17. java斗地主socket_纯JAVA写的socket局域网斗地主游戏
  18. 虚拟机中修改虚拟网络编辑器无效
  19. 可恶的RunDll广告怎么关闭
  20. draft https://www.cnblogs.com/shadow-wolf/p/6524603.html

热门文章

  1. rust中i32转[i8]和[u8]转i32
  2. 如何和有好感的相亲对象推进关系
  3. Java 中Int转String的三种方法
  4. android微信使用62数据免验证,微信62数据提取(非按键写法来个大神转换下!)...
  5. scribe php,Scribe日志收集系统的安装
  6. 着色器语言(GLSL)基础学习三
  7. 记录一个python小白写问卷星定时答题代码的过程
  8. 页面跳转forward和redirect两者区别
  9. Texstudio使用技巧(环境配置、快捷键、源码与PDF相互定位等)
  10. js模拟a标签实现下载附件