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);
    }
  });
}  
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章