Mapbox 实现画圆并高亮圆内要素

需求

Mapbox官方示例只实现了点选和框选的空间查询方式,而在实际开发中也需要实现画圆并高亮圆内的要素。

实现思路

  1. 使用Canvas实现动态画圆
  2. 根据圆心和鼠标所在位置,计算出半径,在map上创建circle图层
  3. 通过turf工具函数,查询与圆相交或在圆内的要素

预览效果

与圆相交的要素:
在这里插入图片描述
圆内要素:
在这里插入图片描述

实现代码

<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8" />
  <title>Highlight features within a circle</title>
  <meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />
  <link href="https://api.mapbox.com/mapbox-gl-js/v2.2.0/mapbox-gl.css" rel="stylesheet">
  <script src="https://api.mapbox.com/mapbox-gl-js/v2.2.0/mapbox-gl.js"></script>
  <script src="https://unpkg.com/@turf/turf@6/turf.min.js"></script>
  <style>
    body {
      margin: 0;
      padding: 0;
    }

    #map {
      position: absolute;
      top: 0;
      bottom: 0;
      width: 100%;
    }

    .boxdraw {
      background: rgba(56, 135, 190, 0.1);
      border: 2px solid #3887be;
      border-radius: 50%;
      position: absolute;
      top: 0;
      left: 0;
      width: 0;
      height: 0;
    }
</style>
</head>

<body>
  <div id="map"></div>

  <script>
    mapboxgl.accessToken = 'your accessToken';
    var map = new mapboxgl.Map({
      container: 'map',
      style: 'mapbox://styles/mapbox/streets-v11',
      center: [-98, 38.88],
      minZoom: 2,
      zoom: 3
    });

    // Disable default box zooming.
    map.boxZoom.disable();

    // Create a popup, but don't add it to the map yet.
    var popup = new mapboxgl.Popup({
      closeButton: false
    });

    map.on('load', function () {
      var canvas = map.getCanvasContainer();

      // Variable to hold the starting xy coordinates
      // when `mousedown` occured.
      var start;

      // Variable to hold the current xy coordinates
      // when `mousemove` or `mouseup` occurs.
      var current;

      // Variable for the draw box element.
      var box;

      // Add the source to query. In this example we're using
      // county polygons uploaded as vector tiles
      map.addSource('counties', {
        'type': 'vector',
        'url': 'mapbox://mapbox.82pkq93d'
      });

      map.addLayer({
          'id': 'counties',
          'type': 'fill',
          'source': 'counties',
          'source-layer': 'original',
          'paint': {
            'fill-outline-color': 'rgba(0,0,0,0.1)',
            'fill-color': 'rgba(0,0,0,0.1)'
          }
        },
        'settlement-label'
      ); // Place polygon under these labels.

      // 高亮图层
      map.addLayer({
          'id': 'counties-highlighted',
          'type': 'fill',
          'source': 'counties',
          'source-layer': 'original',
          'paint': {
            'fill-outline-color': '#484896',
            'fill-color': '#6e599f',
            'fill-opacity': 0.75
          },
          'filter': ['in', 'FIPS', '']
        },
        'settlement-label'
      ); // Place polygon under these labels.

      // 圆圈图层
      map.addSource('circle', {
        'type': 'geojson',
        // 空的geojson数据
        'data': turf.featureCollection([])
      });

      map.addLayer({
          'id': 'circle',
          'type': 'fill',
          'source': 'circle',
          'paint': {
            'fill-outline-color': '#f00',
            'fill-color': 'rgba(56, 135, 190, 0.1)',
          },
        },
        'settlement-label'
      );

      // Set `true` to dispatch the event before other functions
      // call it. This is necessary for disabling the default map
      // dragging behaviour.
      canvas.addEventListener('mousedown', mouseDown, true);

      // Return the xy coordinates of the mouse position
      function mousePos(e) {
        return new mapboxgl.Point(
          e.clientX - canvas.clientLeft,
          e.clientY - canvas.clientTop
        );
      }

      function mouseDown(e) {
        if (e.button !== 0) return;

        // Disable default drag zooming when the shift key is held down.
        map.dragPan.disable();

        // Call functions for the following events
        document.addEventListener('mousemove', onMouseMove);
        document.addEventListener('mouseup', onMouseUp);

        // Capture the first xy coordinates
        start = mousePos(e);
      }

      function onMouseMove(e) {
        // Capture the ongoing xy coordinates
        current = mousePos(e);

        // Append the box element if it doesnt exist
        if (!box) {
          box = document.createElement('div');
          box.classList.add('boxdraw');
          canvas.appendChild(box);
        }

        const r = Math.sqrt(Math.pow(Math.abs(current.x - start.x), 2) + Math.pow(Math.abs(current.y - start.y),
        2));
        const pitch = map.getPitch();
        box.style.transform = 'translate(-50%,-50%) rotateX(' + pitch + 'deg)';
        box.style.left = start.x + 'px';
        box.style.top = start.y + 'px';
        box.style.width = r * 2 + 'px';
        box.style.height = r * 2 + 'px';
      }

      function onMouseUp(e) {
        // Capture xy coordinates
        finish(start, mousePos(e));
      }

      function finish(from, to) {
        // Remove these events now that finish has been called.
        document.removeEventListener('mousemove', onMouseMove);
        document.removeEventListener('mouseup', onMouseUp);

        if (box) {
          box.parentNode.removeChild(box);
          box = null;
        }

        // 屏幕坐标转换
        const fromCoord = map.unproject([from.x, from.y]),
          toCoord = map.unproject([to.x, to.y]);
        const fromPoint = turf.point([fromCoord.lng, fromCoord.lat]),
          toPoint = turf.point([toCoord.lng, toCoord.lat]);
        const distance = turf.distance(fromPoint, toPoint);

        // 将数据渲染到圆圈图层
        const circle = turf.circle(fromPoint, distance);
        map.getSource('circle').setData(turf.featureCollection([circle]));

        // If bbox exists. use this value as the argument for `queryRenderedFeatures`
        if (circle) {
          const bbox = turf.bbox(circle);

          // 从source中获取features数据
          var features = map.querySourceFeatures('counties', {
            sourceLayer: 'original',
          });

          var intersectFeatures = [];
          features.forEach(f => {
            // 判断是否相交
            if (turf.intersect(f, circle)) {
              intersectFeatures.push(f);
            }
            // if (turf.booleanOverlap(f, circle)) {
            //   intersectFeatures.push(f);
            // }

            // 判断是否在圆圈内
            // var inCircle = false;
            // var explode = turf.explode(f);
            // for (let p of explode.features) {
            //   if (turf.booleanPointInPolygon(p, circle)) {
            //     inCircle = true;
            //   } else {
            //     inCircle = false
            //     break;
            //   }
            // }
            // if (inCircle) {
            //   intersectFeatures.push(f);
            // }
          });

          // Run through the selected features and set a filter
          // to match features with unique FIPS codes to activate
          // the `counties-highlighted` layer.
          var filter = intersectFeatures.reduce(
            function (memo, feature) {
              memo.push(feature.properties.FIPS);
              return memo;
            },
            ['in', 'FIPS']
          );

          map.setFilter('counties-highlighted', filter);
        }

        map.dragPan.enable();
      }

      map.on('mousemove', function (e) {
        var features = map.queryRenderedFeatures(e.point, {
          layers: ['counties-highlighted']
        });
        // Change the cursor style as a UI indicator.
        map.getCanvas().style.cursor = features.length ? 'pointer' : '';

        if (!features.length) {
          popup.remove();
          return;
        }

        var feature = features[0];

        popup
          .setLngLat(e.lngLat)
          .setText(feature.properties.COUNTY)
          .addTo(map);
      });
    });
</script>

</body>

</html>

相关链接

Mapbox官方高亮框选范围内要素示例

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实现Mapbox中的框选查找,可以按照以下步骤进行操作: 1. 首先,在Mapbox Studio中创建或选择一个合适的地图样式。可以使用Mapbox Studio Classic或Mapbox Studio进行样式的创建和编辑。 2. 在地图样式中,确保已经添加了所需的地图图层和数据源。这些图层可以是矢量图层、栅格图层或栅格切片图层,根据具体的需求进行选择。 3. 在Mapbox.js中,使用适当的代码库和API来实现框选查找功能。可以使用Mapbox GL JS、Mapbox.js、或者其他适用于你的项目的JavaScript库。 4. 使用合适的交互事件和工具,在地图上创建一个框选区域。例如,可以使用鼠标拖拽事件或触摸事件来创建一个矩形框选区域。 5. 在框选事件中,获取框选区域的坐标范围。可以使用Mapbox的API来获取框选区域的边界坐标。 6. 使用Mapbox的API或其他适用的方法,将框选区域的坐标范围传递给后端服务器或数据库进行查找操作。 7. 在后端服务器或数据库中,根据框选区域的坐标范围执行查询操作。可以使用Mapbox的Map Matching API来执行地理位置匹配操作,根据给定的坐标范围查找符合条件的地理位置点。 8. 将查询结果返回到前端,并在地图上展示查找到的地理位置点。可以使用Mapbox的API或其他适用的方法,在地图上标记或高亮显示查找到的地理位置点。 总结一下,实现Mapbox中的框选查找功能,需要在Mapbox Studio中创建地图样式,使用Mapbox.js或其他适用的JavaScript库来实现框选交互,将框选区域的坐标范围传递给后端进行查询操作,最后在地图上展示查询结果。具体的实现方式可以根据具体的需求和项目进行选择和调整。 Mapbox Studio样式作为Mapbox.js中的底图 Mapbox Studio手册 Mapbox API开发者页面
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值