mapbox相关操作

446 阅读5分钟

以下所有代码在vue3项目中所调试的,如果是其它框架或者原生,稍微修改即可

 // 初始化,
 import mapboxgl, { Map, MapMouseEvent, Point } from 'mapbox-gl';
 ​
 mapboxgl.accessToken='pk.eyJ1Ijoic3o2NjY2NjYiLCJhIjoiY2tuam44NXZzMDEwMzJ1cGV3djR6OHA5cCJ9.2LA3YOPHRLTTB25CvAoIdw';
 ​
 const init = ()=>{
     map = new mapboxgl.Map({
         container:mapRef.value,  // 容器div
           //   style: 'mapbox://styles/mapbox/satellite-streets-v11',
         center: [120.20821745140512, 30.301670444054523],
         zoom: 14, // 地图起始比列
         maxZoom: 17,
     })
 }
 ​

 // 添加数据源
     map.addSource('pointSource', {
       type: 'geojson',
       data: {
          type: 'FeatureCollection',
          features:[
             {
               type:"Feature",
               geometry:{
                 type:'Point',
                 coordinates:[-122.486052, 37.830348]
               },
               properties:{
                 color:'red',
               }
             }     
          ]
       }
     });
 ​
 // 添加图层
 map.addLayer({
     id:'pointSource',
     type:'circle',  // 这里如果是添加图片的话改为symbol
     source:'pointSource',
     paint:{
        'circle-radius':6,
     }
 })

线

 // 添加数据源
     map.addSource('lineSource', {
       type: 'geojson',
       data: {
          type: 'FeatureCollection',
          features:[
             {
               type:"Feature",
               geometry:{
                 type:'LineString',
                 coordinates: [
                 [-122.486052, 37.830348],
                 [-122.456052, 37.810348]
                 ]
               },
               properties:{
                 color:'red',
               }
             }     
          ]
       }
     });
 ​
 // 添加图层
 map.addLayer({
     id:'lineSource',
     type:'line',    // 这里如果是添加图片的话改为symbol
     source:'lineSource',
     layout: {
       'line-join': 'round',
       'line-cap': 'round'
     },
     paint: {
         "line-color": [ // 动态线色
               "match",
               ["get", "stage"], // 获取 stage 字段的值
                "0",
               "red", // 当 stage 为 0 时,线的颜色为蓝色
                "1",
                "blue", // 当 stage 为 1 时,线的颜色为红色
                "gray", // 默认颜色,如果 stage 不是 0 或 1,则使用灰色
                ],
         "line-width": [  // 动态线宽
                "case",
                 ["==", ["get", "stage"], "0"],
                  3,
                 ["==", ["get", "stage"], "1"],
                  2,
                  4,
                 ],
     }
 })
 ​
 //--------------------------------------------------------------
 // 渐变线
   map.addSource("track-source", {
     type: "geojson",
     lineMetrics: true,      // 需要在数据源中开启这个属性
     data: trackLine
   });
 // 在图层paint属性中
     paint: {
         "line-gradient": [
         "interpolate",
         ["linear"],
         ["line-progress"],
         0,
         "blue",
         0.1,
         "royalblue",
         0.3,
         "cyan",
         0.5,
         "lime",
         0.7,
         "yellow",
         1,
         "red"
       ],
     }

 // 添加数据源
     map.addSource('polygonsSource', {
       type: 'geojson',
       data: {
          type: 'FeatureCollection',
          features:[
             {
               type:"Feature",
               geometry:{
                 type:'Polygon',
                 coordinates: [[
                  [-122.486052, 37.830348],
                  [-122.466052, 37.820348],
                  [-122.456052, 37.810348],
                  [-122.486052, 37.830348]
                 ]]
               },
               properties:{
                 color:'red',
               }
             }     
          ]
       }
     });
 ​
 // 添加图层
 map.addLayer({
     id:'polygonsSource',
     type:'fill',    // 这里如果是添加图片的话改为symbol
     source:'polygonsSource',
     paint: {
       'fill-color': '#007cbf',
       'fill-opacity': 0.4
     }
 })

地面垂直线

 // 这里利用了turf.js将一个点转为圆
 const radius = 500;
 const center = [x,y];
 const options: any = {
     steps: 4,
     units: 'centimeters', // 这里单位可以改
     properties: { height: 300, color: 'red' },  // 这里自定义高度
 };
 const circle = turf.circle(center, radius, options); // 变为一个要素
 // 处理要素,自行处理
 features.push(circle)
 ​
 ​
 // 数据源
 map.addSource('vertifyLayer', {
     type: 'geojson',
     data: {  //
       type: 'FeatureCollection',
       features: [],
      },
    });
 ​
 // 图层
 map.addLayer({
     id: 'vertifyLayer',
     type: 'fill-extrusion',
     source: 'vertifyLayer',
     paint: {
         'fill-extrusion-color': ['get', 'color'],  // 圆柱颜色
         'fill-extrusion-height': ['get', 'height'], // 动态获取每个要素的高度
         'fill-extrusion-base': 0,   // 三维填充的底部高度
         'fill-extrusion-opacity': 0.6,  // 三维填充的不透明度(可选,取值范围为 0 ~ 1,默认值为 1)
       },
     });

修改数据源

 // vertifyLayer 为数据源的id 
 map.getSource('vertifyLayer').setData({
     type: 'FeatureCollection',
     features: [],
 });

地图点击事件

 map.on('click',(e)=>{       // 这个可以添加第二个参数,为需要点击的图层
     console.log( [e.lngLat.lng, e.lngLat.lat] )
 })

修改图层属性

 map.setLayoutProperty(
       `xxxx`,   // 图层id
        "visibility",    //属性
        "visible"    // 需要设置的值
 );

点击获取图层要素

 map.on('click',(e)=>{
     const features = map.queryRenderedFeatures(e.point); // 根据点击的经纬度获取要素
     const feature = features[0];
     const layerId = feature.layer.id; // 获取id
     const properties = feature.properties;  // 获取properties
 })
 ​
 // 也可以接收第二个参数
 map.queryRenderedFeatures(e.point, {
 layers: ["points"]
 })
 //获取指定位置上渲染的所有要素。
 //限定查询范围为名为 "points" 的图层
 //也可以是数据源id,为字符串'xxxx'
 // --------------------------------------------------------------------------------
 //完整如下,都为可选参数
 map.queryRenderedFeatures(
   [geometry],//一个包含 type 和 coordinates 属性的对象,用于指定查询范围的几何形状(Point、LineString、Polygon)
   {
     filter: [filterExpression], //一个数组,用于过滤查询结果
     layers: [layerIDs],//一个字符串数组,用于指定要查询的图层 ID
     maxFeatures: number,//一个数字,用于限制查询结果的最大数量
     source: sourceID,// 一个字符串,用于指定要查询的数据源 ID
     validate: boolean// 一个布尔值,用于控制是否对参数进行验证
   }
 )

获取图层对象数据源

 // 返回指定 ID 为 'ccc' 的图层对象,如果该图层不存在则会返回 undefined。
 map.getLayer('xxx')
 // 获取数据源
 map.getSource("points")._data

原生点击屏幕坐标转为经纬度

 // div点击事件
 const handleClick =(event)=>{
    const bbox = [
     [event.clientX, event.clientY],
     [event.clientX + 5, event.clientY + 5], // 第二个暂时应该用不对
   ];
 }
 ​
 // 该方法用于将屏幕坐标系(以像素为单位)转换为地图坐标系(以经纬度为单位)
 // 输入一个包含 x 和 y 属性的对象,表示屏幕坐标,输出一个 LngLat 对象,表示地图坐标。
 let lngLat = map.unproject([event.clientX, event.clientY]);
 ​
 // 该方法用于将地图坐标系(以经纬度为单位)转换为屏幕坐标系(以像素为单位)
 // 输入一个 LngLat 对象,表示地图坐标,输出一个包含 x 和 y 属性的对象,表示屏幕坐标。
 let points = map.project(lngLat)
 ​
 // 根据点击的经纬度获取要素
 const features = map.queryRenderedFeatures(points);
 const feature = features[0];

原生鼠标滚轮事件调用地图滚动

 // @wheel = "handleWheel"
 const handleWheel =(event)=>{
     event.stopPropagation();
 }
 ​
 // mapbox设置图层
 map.zoomTo(map.getZoom() - event.deltaY * 0.01);

原生鼠标滑动事件调用地图滑动

 //1. 给div绑定以下三个方法
     @mousemove.stop="handleMouseMove"       // 鼠标开始移动
     @mousedown.stop="handleDragStart"       // 鼠标移动结束
     @mouseup.stop="handleDragEnd"           // 鼠标松开
 ​
 // 2. 需要一个值来判断鼠标是否按下松开,(mapbox移动地图逻辑是按下鼠标左键拖动地图,松开左键结束拖动)
 let isDragging = false
 let lastPointX, lastPointY; // 鼠标点击的经纬度
 ​
 // 滑动事件
 const handleMouseMove = (event)=>{
       if (!isDragging){
           // 说明没有点击,但是可以做些其它操作,这里我是做了鼠标悬浮操作
           mouseent([event.clientX, event.clientY])
       }else{
           // 触发点击移动事件
            const deltaMove = {
              x: event.clientX - lastPointX,
              y: event.clientY - lastPointY,
          };
           lastPointX = event.clientX;
           lastPointY = event.clientY;
           // 触发滑动事件
           mouseFun(deltaMove)
       }
 }
 ​
 // 鼠标按下事件
 const handleDragStart = (event)=>{
   isDragging = true;  
   lastPointX = event.clientX;  // 赋值经纬度
   lastPointY = event.clientY;
 }
 ​
 // 鼠标松开事件
 const handleDragEnd = (event) => {
   isDragging = false;
 };
 ​
 //------------------------------------------------------------------------------
 // mapbox设置滑动
 const mouseFun = (deltaMove)=>{
   map.panBy([-deltaMove.x, -deltaMove.y], {
     duration: 0,
   });
 }
 ​
 //------------------------------------------------------------------------------
 // 顺便说一下鼠标悬浮操作事件
 let popup = new mapboxgl.Popup({  // 悬浮弹窗
     closeButton: false,
     closeOnClick: false,
   });
 ​
 const mouseent = (val)=>{
   let lngLat = map.unproject([val[0], val[1]]);
   let points = map.project(lngLat);  // 先进行坐标转换
   const features = map.queryRenderedFeatures(points); // 获取要素
   const feature = features[0];  
    // 判断鼠标悬浮的该要素是否为真
     if (feature) {
         const layerId = feature.layer.id;
         const properties = feature.properties;  // 如果为真获取该要素的id以及properties
         // 判断该id是否包含指定的图层id
         if(properties.id && layerId.includes("point-layer1")){
             // 自定义鼠标悬浮弹窗
            popup
           .setLngLat([lngLat.lng, lngLat.lat])
           .addClassName("my-custom-popup")
           .setHTML(`<div>${properties.name}</div>`)
           .addTo(map);
         }else{
           popup.remove(); // 关闭悬浮弹窗
         }
     }
 }
 ​

处理地图数据为url地址

 // 比如接口返回为url,打开该url显示为mapbox需要的geojson数据
 const url = `http://xxxx.xxxxx.xx`
 ​
 fetch(url).then((response)=>response.json()).then((geojson)=>{
     // 这里就能拿到正确的mapbox需要的geojson数据了
 })

创建自定义组件的popup

 let mountNode: any;
 ​
 map.on('click','xxx',(x)=>{
     // 创建该弹窗
     createVideoPopUp(sdf)
     // x,y为经纬度坐标,显示在地图上
     new mapboxgl.Popup({ closeButton: false }).setLngLat(x,y).setDOMContent(mountNode).addTo(map);
 })
 ​
 // features可以为传递的参数
 function createVideoPopUp(features: object) {
   // console.log('features', features)
   const app = createVNode(pop, { obj: features })       // pop为自定义的组件,{ obj: features }为传参
   mountNode = document.createElement("div");
   render(app, mountNode);
   document.body.appendChild(mountNode);
 }
 ​
 // 自定义组件中接收传参
 defineProps({
   obj: {
     type: Object
   }
 })

如果有错误的地方还请大佬指正,还有更好的方法还请大佬指教!