专栏首页GIS讲堂mapboxGL测量实现

mapboxGL测量实现

概述

讲真,MapboxGL里面虽然有测量的功能,但是不太好用,于是就萌生了自己实现的方法。本文几个turf.js来说说mapboxGL中测量的实现。

效果

实现

1、实现思路

  1. 按照绘制的流程,需要涉及到map的三个事件:click,dblclick,mousemove,其中click为绘制,dblclick为结束绘制,mousemove为绘制中。这样,定义一个状态标识isMeasure,在点击开始绘制的按钮的时候,将标识设置为true,在map的三个事件中都会根据这个标识判断是否为绘制状态。
  2. 地图的展示分layer和marker来分别展示;
  3. layer里面区分点和线(面)图层,以达到比较好的展示效果。

2、实现代码

1.样式
.measure-result {
  background-color: white;
  border-radius: 3px;
  height: 16px;
  line-height: 16px;
  padding: 0 3px;
  font-size: 12px;
  box-shadow: 0 0 0 1px #ccc;
  &.close {
    cursor: pointer;
    width: 14px;
    height: 14px;
    line-height: 16px;
    text-align: center;
    padding: 0;
  }
}
2.测量距离
function measureLength() {
  var isMeasure = true;
  // 禁止双击缩放
  map.doubleClickZoom.disable();
  map.getCanvas().style.cursor = 'default';

  function clearMeasure() {
    $(".measure-result").remove();
    var source = map.getSource('points');
    var json = {
      'type': 'FeatureCollection',
      'features': []
    };
    if(source) {
      map.getSource('points').setData(json);
      map.getSource('line-move').setData(json);
      map.getSource('line').setData(json);
    }
    var sourceArea = map.getSource('points-area');
    if(sourceArea) {
      map.getSource('points-area').setData(json);
      map.getSource('line-area').setData(json);
    }
  }

  clearMeasure();

  var jsonPoint = {
    'type': 'FeatureCollection',
    'features': []
  };
  var jsonLine = {
    'type': 'FeatureCollection',
    'features': []
  };
  var points = [];
  const ele = document.createElement('div');
  ele.setAttribute('class', 'measure-result');
  const option = {
    element: ele,
    anchor: 'left',
    offset: [8, 0]
  };
  var tooltip = new mapboxgl.Marker(option)
          .setLngLat([0, 0])
          .addTo(map);
  var markers = [];

  var source = map.getSource('points');
  if(source) {
    map.getSource('points').setData(jsonPoint);
    map.getSource('line-move').setData(jsonLine);
    map.getSource('line').setData(jsonLine);
  } else {
    map.addSource('points', {
      type: 'geojson',
      data: jsonPoint
    });
    map.addSource('line', {
      type: 'geojson',
      data: jsonLine
    });
    map.addSource('line-move', {
      type: 'geojson',
      data: jsonLine
    });
    map.addLayer({
      id: 'line-move',
      type: 'line',
      source: 'line-move',
      paint: {
        'line-color': '#ff0000',
        'line-width': 2,
        'line-opacity': 0.65
      }
    });
    map.addLayer({
      id: 'line',
      type: 'line',
      source: 'line',
      paint: {
        'line-color': '#ff0000',
        'line-width': 2,
        'line-opacity': 0.65
      }
    });
    map.addLayer({
      id: 'points',
      type: 'circle',
      source: 'points',
      paint: {
        'circle-color': '#ffffff',
        'circle-radius': 3,
        'circle-stroke-width': 2,
        'circle-stroke-color': '#ff0000'
      }
    });
  }
  function addPoint(coords) {
    if(jsonPoint.features.length > 0) {
      var prev = jsonPoint.features[jsonPoint.features.length - 1];
      jsonLine.features.push({
        type: 'Feature',
        geometry: {
          type: 'LineString',
          coordinates: [prev.geometry.coordinates, coords]
        }
      });
      map.getSource('line').setData(jsonLine);
    }
    jsonPoint.features.push({
      type: 'Feature',
      geometry: {
        type: 'Point',
        coordinates: coords
      }
    });
    map.getSource('points').setData(jsonPoint);
  }

  function getLength(coords) {
    var _points = points.concat([coords]);
    var line = turf.lineString(_points);
    var len = turf.length(line);
    if(len < 1) {
      len = Math.round(len * 1000) + 'm';
    } else {
      len = len.toFixed(2) + 'km';
    }
    return len;
  }

  function addMeasureRes(coords) {
    const ele = document.createElement('div');
    ele.setAttribute('class', 'measure-result');
    const option = {
      element: ele,
      anchor: 'left',
      offset: [8, 0]
    };
    ele.innerHTML = points.length === 0 ? '起点' : getLength(coords);
    var marker = new mapboxgl.Marker(option)
            .setLngLat(coords)
            .addTo(map);
    markers.push(marker);
  }

  map.on('click', function (_e) {
    if(isMeasure) {
      var coords = [_e.lngLat.lng, _e.lngLat.lat];
      addMeasureRes(coords);
      addPoint(coords);
      points.push(coords);
    }
  });

  map.on('mousemove', function (_e) {
    if(isMeasure) {
      var coords = [_e.lngLat.lng, _e.lngLat.lat];
      if (jsonPoint.features.length > 0) {
        var prev = jsonPoint.features[jsonPoint.features.length - 1];
        var json = {
          type: 'Feature',
          geometry: {
            type: 'LineString',
            coordinates: [prev.geometry.coordinates, coords]
          }
        };
        map.getSource('line-move').setData(json);
        ele.innerHTML = getLength(coords);

      } else {
        ele.innerHTML = '点击地图开始测量';
      }
      tooltip.setLngLat(coords);
    }
  });

  map.on('dblclick', function (_e) {
    if(isMeasure) {
      var coords = [_e.lngLat.lng, _e.lngLat.lat];
      addPoint(coords);
      isMeasure = false;
      map.getCanvas().style.cursor = '';
      jsonPoint.features = [];
      jsonLine.features = [];
      tooltip.remove();
      // 添加关闭按钮
      const ele = document.createElement('div');
      ele.setAttribute('class', 'measure-result close');
      const option = {
        element: ele,
        anchor: 'bottom-left',
        offset: [-5, -10]
      };
      ele.innerHTML = '×';
      new mapboxgl.Marker(option)
              .setLngLat(coords)
              .addTo(map);
      ele.onclick = function (__e) {
        __e.stopPropagation();
        map.doubleClickZoom.enable();
        clearMeasure();
      }
    }
  });
}  
3.测量面积
function measureArea() {  
  var isMeasure = true;
  // 禁止双击缩放
  map.doubleClickZoom.disable();
  map.getCanvas().style.cursor = 'default';
  function clearMeasure() {
    $(".measure-result").remove();
    var source = map.getSource('points');
    var json = {
      'type': 'FeatureCollection',
      'features': []
    };
    if(source) {
      map.getSource('points').setData(json);
      map.getSource('line-move').setData(json);
      map.getSource('line').setData(json);
    }
    var sourceArea = map.getSource('points-area');
    if(sourceArea) {
      map.getSource('points-area').setData(json);
      map.getSource('line-area').setData(json);
    }
  }
  clearMeasure();
  var jsonPoint = {
    'type': 'FeatureCollection',
    'features': []
  };
  var jsonLine = {
    'type': 'FeatureCollection',
    'features': []
  };
  var points = [];
  var ele = document.createElement('div');
  ele.setAttribute('class', 'measure-result');
  const option = {
    element: ele,
    anchor: 'left',
    offset: [8, 0]
  };
  var tooltip = new mapboxgl.Marker(option)
          .setLngLat([0, 0])
          .addTo(map);
  var source = map.getSource('points-area');
  if(source) {
    map.getSource('points-area').setData(jsonPoint);
    map.getSource('line-area').setData(jsonLine);
  } else {
    map.addSource('points-area', {
      type: 'geojson',
      data: jsonPoint
    });
    map.addSource('line-area', {
      type: 'geojson',
      data: jsonLine
    });
    map.addLayer({
      id: 'line-area',
      type: 'fill',
      source: 'line-area',
      paint: {
        'fill-color': '#ff0000',
        'fill-opacity': 0.1
      }
    });
    map.addLayer({
      id: 'line-area-stroke',
      type: 'line',
      source: 'line-area',
      paint: {
        'line-color': '#ff0000',
        'line-width': 2,
        'line-opacity': 0.65
      }
    });
    map.addLayer({
      id: 'points-area',
      type: 'circle',
      source: 'points-area',
      paint: {
        'circle-color': '#ffffff',
        'circle-radius': 3,
        'circle-stroke-width': 2,
        'circle-stroke-color': '#ff0000'
      }
    });
  }

  function getArea(coords) {
    var pts = points.concat([coords]);
    pts = pts.concat([points[0]]);
    var polygon = turf.polygon([pts]);
    var area = turf.area(polygon);
    if(area < 1000) {
      area = Math.round(area) + 'm²';
    } else {
      area = (area / 1000000).toFixed(2) + 'km²';
    }
    return area;
  }
  function addPoint(coords) {
    jsonPoint.features.push({
      type: 'Feature',
      geometry: {
        type: 'Point',
        coordinates: coords
      }
    });
    map.getSource('points-area').setData(jsonPoint);
  }
  
  map.on('click', function (_e) {
    if(isMeasure) {
      var coords = [_e.lngLat.lng, _e.lngLat.lat];
      points.push(coords);
      addPoint(coords);
    }
  });

  map.on('dblclick', function (_e) {
    if(isMeasure) {
      var coords = [_e.lngLat.lng, _e.lngLat.lat];
      points.push(coords);
      isMeasure = false;
      ele.innerHTML = getArea(coords);
      tooltip.setLngLat(coords);
      // 添加关闭按钮
      var _ele = document.createElement('div');
      _ele.setAttribute('class', 'measure-result close');
      var option = {
        element: _ele,
        anchor: 'bottom-left',
        offset: [-5, -10]
      };
      _ele.innerHTML = '×';
      new mapboxgl.Marker(option)
              .setLngLat(coords)
              .addTo(map);
      _ele.onclick = function (__e) {
        __e.stopPropagation();
        map.doubleClickZoom.enable();
        clearMeasure();
      }
    }
  });

  map.on('mousemove', function (_e) {
    if(isMeasure) {
      var coords = [_e.lngLat.lng, _e.lngLat.lat];
      var len = jsonPoint.features.length;
      if (len === 0) {
        ele.innerHTML = '点击地图开始测量';
      } else if (len ===1) {
        ele.innerHTML = '点击地图继续绘制';
      } else {
        var pts = points.concat([coords]);
        pts = pts.concat([points[0]]);
        var json = {
          type: 'Feature',
          geometry: {
            type: 'Polygon',
            coordinates: [pts]
          }
        };
        map.getSource('line-area').setData(json);
        ele.innerHTML = getArea(coords);
      }
      tooltip.setLngLat(coords);
    }
  });
}  

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • geotools获取给定点的DEM高程值

    1、在web端绘制一条曲线; 2、获取各节点处的高程值; 3、根据高程值绘制高程堆积图。

    lzugis
  • 实现地图天气预报的显示

    概述:很多时候,会用到地图与天气预报的相结合显示,本文结合web天气插件,实现地图天气预报的结合现实。

    lzugis
  • Arcgis for JS之地图自适应调整

    概述:本节讲述的内容为当浏览器大小发生变化或者地图展示区域的大小发生变化时,地图的自适应调整。地图的自适应常见于以下几种情况:1、系统中有收缩或者全屏的按钮;2...

    lzugis
  • 2019-04-19 程序员都应该搞懂的概念【Mysql核心技术】聊聊事务的实现原理

    https://juejin.im/post/5cb2e3b46fb9a0686e40c5cb?utm_source=gold_browser_extensio...

    Albert陈凯
  • 活动安排问题--贪心算法

    活动安排问题就是要在所给的活动集合中选出最大的相容活动子集合,是可以用贪心算法有效求解的很好例子。该问题要求高效地安排一系列争用某一公共资源的活动。贪心算法提...

    用户1154259
  • python代码规范

    虽然说实现功能是编程的首要目的,但是在实际开发过程中,代码的可读性也是非常重要的。不同的人有不同的编程习惯和风格,对于一个团队而言,想要高效的协同开发,遵守一个...

    生信修炼手册
  • Go之定义变量

    院长技术
  • v8源码解析之hashmap(基于0.1.5)

    theanarkh
  • Spring Cloud Stream如何处理消息重复消费?

    最近收到好几个类似的问题:使用Spring Cloud Stream操作RabbitMQ或Kafka的时候,出现消息重复消费的问题。通过沟通与排查下来主要还是用...

    程序猿DD
  • serverless+腾讯云短信实现短信验证码登录

    传统开发协作流程(多角色参与): 1.后台开发:短信接口发送短信API、校验短信验证码是否有效API、存储用户验证状态 2.运维开发:接口部署,容灾 3.前端(...

    腾讯即时通信IM

扫码关注云+社区

领取腾讯云代金券