openlayers 测量功能实现(测距测面)- vue3

一、配置openlayer环境

借鉴:Vue 3 + OpenLayers 的简单使用_vue3 openlayers-CSDN博客

二、代码如下(测距、测面和清除)

measurs.js:

import {ref} from 'vue';
import Draw from 'ol/interaction/Draw'
import VectorSource from 'ol/source/Vector';
import VectorLayer from 'ol/layer/Vector';
import Point from "ol/geom/Point";
import {
  unByKey
} from 'ol/Observable.js';
import Overlay from 'ol/Overlay';
import {Feature } from "ol";
// import {
//   getArea,
//   getLength
// } from 'ol/sphere.js';
import {getLength} from 'ol/sphere';
import {getArea} from 'ol/sphere';
import LineString from 'ol/geom/LineString';
import Polygon from 'ol/geom/Polygon';
import {
  Circle as CircleStyle,
  Fill,
  Stroke,
  Style
} from 'ol/style.js';


// 测距所需对象
const lineVectorLayer = ref(null);
const lineDraw = ref(null);

// 测面所需对象
const areaVectorLayer = ref(null);
const areaDraw = ref(null);



// 测距
export const measureLine = (map) => {
  clearMeasure(map);//清空测量图层
  // 创建数据源
  var source = new VectorSource();

  lineVectorLayer.value = new VectorLayer({
    id:'Line',
    source: source,
    style: new Style({
      fill: new Fill({
        color: 'rgba(255, 255, 255, 0.2)'
      }),
      stroke: new Stroke({
        color: 'red',
        width: 2
      }),
      image: new CircleStyle({
        radius: 7,
        fill: new Fill({
          color: '#ffcc33'
        })
      })
    }),
    zIndex:16
  });
  
  map.addLayer(lineVectorLayer.value)

  /**
   * Currently drawn feature.
   * @type {module:ol/Feature~Feature}
   */
  var sketch;


  /** 测距
   * The help tooltip element.
   * @type {Element}
   */
  var lineHelpTooltipElement;


  /**
   * Overlay to show the help messages.
   * @type {module:ol/Overlay}
   */
  var lineHelpTooltip;


  /**
   * The measure tooltip element.
   * @type {Element}
   */
  var measureTooltipElement;


  /**
   * Overlay to show the measurement.
   * @type {module:ol/Overlay}
   */
  var measureTooltip;



  /**
   * Message to show when the user is drawing a line.
   * @type {string}
   */
  var continueLineMsg = '';

  createMeasureTooltip();
  createHelpTooltip();

  /**
   * Handle pointer move.
   * @param {module:ol/MapBrowserEvent~MapBrowserEvent} evt The event.
   */
  var pointerMoveHandler = function (evt) {
    if (evt.dragging) {
      return;
    }
    /** @type {string} */
    var helpMsg = '请点击开始测距';

    if (sketch) {
      // 测量时的提示文字
      var geom = (sketch.getGeometry());
      if (geom instanceof LineString) {
        helpMsg = continueLineMsg;
      }
       
    }

    // 设置提示对话框
    //lineHelpTooltipElement.innerHTML = helpMsg;// 文字没有颜色
    lineHelpTooltipElement.innerHTML = "<span style='color: red;'>"+helpMsg+"</span>";
    lineHelpTooltip.setPosition(evt.coordinate);

    lineHelpTooltipElement.classList.remove('hidden');
  };
  // 监听鼠标移动方法
  map.on('pointermove', pointerMoveHandler);

  map.getViewport().addEventListener('mouseout', function () {
    lineHelpTooltipElement.classList.add('hidden');
  });

  var draw;// 绘制对象

  var formatLength = function (line) {
      //获取投影坐标系
      var sourceProj = map.getView().getProjection();
      //ol/sphere里有getLength()和getArea()用来测量距离和区域面积,默认的投影坐标系是EPSG:3857, 其中有个options的参数,可以设置投影坐标系
      var length = getLength(line, {projection: sourceProj});
      //var length = getLength(line);
      var output;
      if (length > 100) {
        output = (Math.round(length / 1000 * 100) / 100) +
          ' ' + 'km';
      } else {
        output = (Math.round(length * 100) / 100) +
          ' ' + 'm';
      }
      return output;
    };


  // 获取存放feature的vectorlayer层。map初始化的时候可以添加好了
  for(let layerTmp of map.getLayers().getArray()){
    if(layerTmp.get("name")=="feature"){
      source = layerTmp.getSource();
    }
  }

   // 测量距离
  function addLineInteraction() {
    var type = "LineString";
    draw = new Draw({
      source: source,
      type: type,
      style: new Style({
        fill: new Fill({
          color: 'rgba(255, 255, 255, 0.2)'
        }),
        stroke: new Stroke({
          color: 'rgba(0, 200, 255, 0.5)',
          lineDash: [10, 10],
          width: 2
        }),
        image: new CircleStyle({
          radius: 5,
          stroke: new Stroke({
            color: 'rgba(0, 200, 255, 0.7)'
          }),
          fill: new Fill({
            color: 'rgba(255, 255, 255, 0.2)'
          })
        })
      })
    });
  // 赋值
  lineDraw.value = draw;
  map.addInteraction(lineDraw.value);
    
    var listener;
    draw.on('drawstart',
      function (evt) {
        // set sketch
        sketch = evt.feature;

        /** @type {module:ol/coordinate~Coordinate|undefined} */
        var tooltipCoord = evt.coordinate;

        listener = sketch.getGeometry().on('change', function (evt) {
          var geom = evt.target;
          var output;
          if (geom instanceof LineString) {
            output = formatLength(geom);
            tooltipCoord = geom.getLastCoordinate();
          }
          //measureTooltipElement.innerHTML = output;// 文字没有颜色
          measureTooltipElement.innerHTML = "<span style='color: red;'>"+output+"</span>";
          measureTooltip.setPosition(tooltipCoord);
        });

        //地图双击事件
        map.on('dblclick', function (evt) {
            var point = new Point(evt.coordinate);
            source.addFeature(new Feature(point));
        });

      }, this);


    draw.on('drawend',
      function () {
        measureTooltipElement.className = 'tooltip tooltip-static';
        measureTooltip.setOffset([0, -7]);
        // unset sketch
        sketch = null;
        // unset tooltip so that a new one can be created
        measureTooltipElement = null;
        createMeasureTooltip();
        unByKey(listener);
        map.un('pointermove', pointerMoveHandler);
        map.removeInteraction(draw);
        lineHelpTooltipElement.classList.add('hidden');
      }, this);
  }

  function createHelpTooltip() {
    if (lineHelpTooltipElement) {
      lineHelpTooltipElement.parentNode.removeChild(lineHelpTooltipElement);
    }
    lineHelpTooltipElement = document.createElement('div');
    lineHelpTooltipElement.className = 'tooltip hidden';
    lineHelpTooltip = new Overlay({
      element:lineHelpTooltipElement,
      offset: [15, 0],
      positioning: 'center-left',
    });
    map.addOverlay(lineHelpTooltip);
  }

  function createMeasureTooltip() {
    if (measureTooltipElement) {
      measureTooltipElement.parentNode.removeChild(measureTooltipElement);
    }
    measureTooltipElement = document.createElement('div');
    measureTooltipElement.className = 'tooltip tooltip-measure';
    measureTooltip = new Overlay({
      element: measureTooltipElement,
      offset: [0, -15],
      positioning: 'bottom-center'
    });
    map.addOverlay(measureTooltip);
  }

  // 量测调用
  addLineInteraction();
}

// 测面
export const measurePolygon = (map) =>{
  clearMeasure(map);//清空测量图层
  // 创建数据源
  var source = new VectorSource();
 
  areaVectorLayer.value = new VectorLayer({
    id:'Area',
    source: source,
    style: new Style({
      fill: new Fill({
        color: 'rgba(255, 255, 255, 0.2)'
      }),
      stroke: new Stroke({
        color: 'red',
        width: 2
      }),
      image: new CircleStyle({
        radius: 7,
        fill: new Fill({
          color: '#ffcc33'
        })
      })
    }),
    zIndex:16
  });

  map.addLayer(areaVectorLayer.value);

  /**
   * Currently drawn feature.
   * @type {module:ol/Feature~Feature}
   */
  var sketch;


  /**
   * The help tooltip element.
   * @type {Element}
   */
  var areaHelpTooltipElement;


  /**
   * Overlay to show the help messages.
   * @type {module:ol/Overlay}
   */
  var areaHelpTooltip;


  /**
   * The measure tooltip element.
   * @type {Element}
   */
  var measureTooltipElement;


  /**
   * Overlay to show the measurement.
   * @type {module:ol/Overlay}
   */
  var measureTooltip;


  /**
   * Message to show when the user is drawing a polygon.
   * @type {string}
   */
  var continuePolygonMsg = '';


  createMeasureTooltip();
  createHelpTooltip();

  /**
   * Handle pointer move.
   * @param {module:ol/MapBrowserEvent~MapBrowserEvent} evt The event.
   */
  var pointerMoveHandler = function (evt) {
    if (evt.dragging) {
      return;
    }
    /** @type {string} */
    var helpMsg = '请点击开始测面';

    if (sketch) {
      var geom = (sketch.getGeometry());
      if (geom instanceof Polygon) {
        helpMsg = continuePolygonMsg;
      }
    }

    //areaHelpTooltipElement.innerHTML = helpMsg;//没有颜色
    areaHelpTooltipElement.innerHTML = "<span style='color: red;'>"+helpMsg+"</span>";
    areaHelpTooltip.setPosition(evt.coordinate);

    areaHelpTooltipElement.classList.remove('hidden');
  };
  // 监听鼠标移动方法
  map.on('pointermove', pointerMoveHandler);

  map.getViewport().addEventListener('mouseout', function () {
    areaHelpTooltipElement.classList.add('hidden');
  });

  var draw;


var formatArea = function (polygon) {
  //获取投影坐标系
  var sourceProj = map.getView().getProjection();
  var area = getArea(polygon, {projection: sourceProj})
  //var area = getArea(polygon);
  //console.info(area)
  var output;
  if (area > 10000) {
    output = (Math.round(area / 1000000 * 100) / 100) +
      ' ' + 'km<sup>2</sup>';
  } else {
    output = (Math.round(area * 100) / 100) +
      ' ' + 'm<sup>2</sup>';
  }
  return output;
};
 
  // 获取存放feature的vectorlayer层。map初始化的时候可以添加好了
  for(let layerTmp of map.getLayers().getArray()){
    if(layerTmp.get("name")=="feature"){
      // layer = layerTmp;
      // layerTmp.setSource(null)
      source = layerTmp.getSource();
    }
  }

  // 测量面
  function addAreaInteraction() {
    
    var type = "Polygon";
    draw = new Draw({
      source: source,
      type: type,
      style: new Style({
        fill: new Fill({
          color: 'rgba(255, 255, 255, 0.2)'
        }),
        stroke: new Stroke({
          color: 'rgba(0, 200, 255, 0.5)',
          lineDash: [10, 10],
          width: 2
        }),
        image: new CircleStyle({
          radius: 5,
          stroke: new Stroke({
            color: 'rgba(0, 200, 255, 0.7)'
          }),
          fill: new Fill({
            color: 'rgba(255, 255, 255, 0.2)'
          })
        })
      })
    });
    //赋值
    areaDraw.value = draw;
    map.addInteraction(areaDraw.value);

    var listener;
    draw.on('drawstart',
      function (evt) {
        // set sketch
        sketch = evt.feature;

        /** @type {module:ol/coordinate~Coordinate|undefined} */
        var tooltipCoord = evt.coordinate;

        listener = sketch.getGeometry().on('change', function (evt) {
          var geom = evt.target;
          var output;
          if (geom instanceof Polygon) {
            output = formatArea(geom);
            tooltipCoord = geom.getInteriorPoint().getCoordinates();
          } 
          //measureTooltipElement.innerHTML = output;// 没有颜色
          measureTooltipElement.innerHTML = "<span style='color: red;'>"+output+"</span>";
          measureTooltip.setPosition(tooltipCoord);
        });

        //地图双击事件-绘制结束时有个小黄点
       /* map.on('dblclick', function (evt) {
            var point = new Point(evt.coordinate);
            source.addFeature(new Feature(point));
        });*/



      }, this);


    draw.on('drawend',
      function () {
        measureTooltipElement.className = 'tooltip tooltip-static';
        measureTooltip.setOffset([0, -7]);
        // unset sketch
        sketch = null;
        // unset tooltip so that a new one can be created
        measureTooltipElement = null;
        createMeasureTooltip();
        unByKey(listener);
        map.un('pointermove', pointerMoveHandler);
        map.removeInteraction(draw);
        areaHelpTooltipElement.classList.add('hidden');
      }, this);
  }



  function createHelpTooltip() {
    if (areaHelpTooltipElement) {
      areaHelpTooltipElement.parentNode.removeChild(areaHelpTooltipElement);
    }
    areaHelpTooltipElement = document.createElement('div');
    areaHelpTooltipElement.className = 'tooltip hidden';
    areaHelpTooltip = new Overlay({
      element: areaHelpTooltipElement,
      offset: [15, 0],
      positioning: 'center-left'
    });
    map.addOverlay(areaHelpTooltip);
  }

  function createMeasureTooltip() {
    if (measureTooltipElement) {
      measureTooltipElement.parentNode.removeChild(measureTooltipElement);
    }
    measureTooltipElement = document.createElement('div');
    measureTooltipElement.className = 'tooltip tooltip-measure';
    measureTooltip = new Overlay({
      element: measureTooltipElement,
      offset: [0, -15],
      positioning: 'bottom-center'
    });
    map.addOverlay(measureTooltip);
  }
  // 量测调用
  addAreaInteraction();
  
}

// 清空测量图层
export const clearMeasure = (map) =>{
  
  // 清除测量距离
  if(lineVectorLayer.value != null){
    // 清除图层
    map.removeLayer(lineVectorLayer.value);
    // 结束测距绘制
    map.removeInteraction(lineDraw.value);
  }

  // 清除测面
  if(areaVectorLayer.value != null){
    // 清除图层
    map.removeLayer(areaVectorLayer.value);
     // 结束测面绘制
    map.removeInteraction(areaDraw.value);
  }
    // 清除overlays层(测量的数值)
    map.getOverlays().clear();
}

三、使用方法

在**vue中引入

import {measureLine,measurePolygon,clearMeasure} from "../js/measure.js"

方法中调用:

// 测距事件
const measureDistance = () =>{
   // alert("测距");
   measureLine(props.map);
}

// 测面事件
const measureArea = () =>{
    //alert("测面");
    measurePolygon(props.map);
}

const clear = () =>{
    //alert("清除");
    clearMeasure(props.map);
}

注意:props.map是map.vue中传递过来的map容器对象

父组件:

<template>
  <div class="openlayers-map">
    <div id="map">
      <BaseVue :map="map" :mapCenter="mapCenter" :mapZoom="mapZoom"/>
    </div>
  </div>
</template>
<script setup>
***
***

const map = ref(null)
const mapCenter = ref([125.313642, 43.898338])
const mapZoom = ref(15) // 默认缩放级别

//配置地图方法
***
***


</script>

子组件:

// 子组件承接父组件对象
const props = defineProps({
    map:Map,
    mapCenter:[],
    mapZoom:Number
});

注意:如果和绘制(点、线、面等)drawTools.js一起使用需要先清除测量绘制图层,使用如下:

// 测距事件
const measureDistance = () =>{
   // 清除绘制
   drawTools.endDraw(props.map);
   measureLine(props.map);
}

// 测面事件
const measureArea = () =>{
    // 清除绘制
    drawTools.endDraw(props.map);
    measurePolygon(props.map);
}

const clear = () =>{
    clearMeasure(props.map);// 清除测量
    drawTools.endDraw(props.map);// 清除绘制
}
相关推荐
老码沉思录8 分钟前
React Native 全栈开发实战班 - 性能与调试之打包与发布
javascript·react native·react.js
MapleLea1f16 分钟前
26届JAVA 学习日记——Day14
java·开发语言·学习·tcp/ip·程序人生·学习方法
小汤猿人类17 分钟前
SpringTask
开发语言·python
爪哇学长24 分钟前
解锁API的无限潜力:RESTful、SOAP、GraphQL和Webhooks的应用前景
java·开发语言·后端·restful·graphql
老赵的博客31 分钟前
QT 自定义界面布局要诀
开发语言·qt
p-knowledge1 小时前
建造者模式(Builder Pattern)
java·开发语言·建造者模式
网络安全(king)1 小时前
【Python】【持续项目】Python-安全项目搜集
开发语言·python·安全
工业甲酰苯胺1 小时前
Python脚本消费多个Kafka topic
开发语言·python·kafka
乐闻x1 小时前
Vue实践篇:如何在 Vue 项目中检测元素是否展示
前端·javascript·vue.js
麻花20131 小时前
C#之WPF的C1FlexGrid空间的行加载事件和列事件变更处理动态加载的枚举值
开发语言·c#·wpf