Vue OpenLayer测距功能的实现


Posted in Vue.js onApril 20, 2022

前言

首先呢说明一下,我是跟着一个大佬学的,所以我是个小次佬,openlayer的官网上面给出了案例,但是习惯vue开发的我完全不理解,关键是连注释都没多少,而且我 openlayer 用的本来就不多。

然后这里分享一下官网的测距案例

引入相关库文件

这个库文件直接按照官网的来就可以了。 首先说一个事情哈,官网用的案例是地图使用的 EPSG:3857, 如果我们改成 EPSG:4326,测量数据不准确,切记这一点。

import 'ol/ol.css';
  import Draw from 'ol/interaction/Draw';
  import Map from 'ol/Map';
  import Overlay from 'ol/Overlay';
  import View from 'ol/View';
  import { Circle as CircleStyle, Fill, Stroke, Style } from 'ol/style';
  import { LineString, Polygon } from 'ol/geom';
  import { OSM, Vector as VectorSource } from 'ol/source';
  import { Tile as TileLayer, Vector as VectorLayer } from 'ol/layer';
  import { getArea, getLength } from 'ol/sphere';
  import { unByKey } from 'ol/Observable';

上面是我引入的库文件,和官网基本上一样。

绘制提示文字

首先我们看下图官网效果,官网开始绘制或者是绘制中都在鼠标旁边有一个title文本框用来提示用户操作信息。

Vue OpenLayer测距功能的实现

我们首先来实现一下这个功能。

首先说明一点哈,这是关键代码,有些参数可能用起来发现没有声明,都是全局的,自己加在全局就可以,主要是下面这一些。

var map = null
  var helpTooltipElement = null
  var feature = null;
  var helpTooltip = null;
  var draw = null;
  var measureTooltipElement = null;
  var measureTooltip = null;
  var listener = null;
  var mapMouseMove = null;

首先我们在需要实现测距功能的页面上写两个按钮,一个开始测距,一个结束测距。然后点击开始测距的时候,执行一个方法,假设是distance方法。

distance() {
        let source = new VectorSource()  // 首先创建一个数据源,用来放置绘制过程中和绘制结束后的线段
        const layer = new VectorLayer({  // 添加一个图层,用来放置数据源,样式自己随便设置就可以了,我这里默认的官网
          source: source,
          style: new Style({
            fill: new Fill({
              color: 'rgba(255, 255, 255, 0.2)',
            }),
            stroke: new Stroke({
              color: '#ffcc33',
              width: 4,
            }),
            image: new CircleStyle({
              radius: 7,
              fill: new Fill({
                color: '#ffcc33',
              }),
            }),
          }),
        });
        mapMouseMove = map.on('pointermove', (ev) => {  // 给地图添加一个鼠标移动事件
          let helpMsg = '点击开始测量'    // 默认开始的操作提示文本
          if (feature) {  // featuer 是全局的,判断有没有点击鼠标绘制过
            helpMsg = '双击结束测量'    // 如果之前点击绘制了就显示双击结束
          }
          helpTooltipElement.innerHTML = helpMsg;   // 设置dom的提示文字
          helpTooltip.setPosition(ev.coordinate);  // 设置位置跟着鼠标走
          helpTooltipElement.classList.remove('hidden')  // 让他显示出来
        })
        this.createHelpTooltip()   // 创建那个helpTooltipElement方法
        map.addLayer(layer)  // 把图层添加到地图
      },

然后调用了一个初始化操作提示的dom元素。这个就是官网的函数,如果参数名和自己起的或者是map的指向问题需要自己根据自己的实际修改一下。

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

还有一点,为了好看,把官网的样式复制一下子。

<style scoped>
  /deep/.ol-tooltip {
    position: relative;
    background: rgba(0, 0, 0, 0.5);
    border-radius: 4px;
    color: white;
    padding: 4px 8px;
    opacity: 0.7;
    white-space: nowrap;
    font-size: 12px;
    cursor: default;
    user-select: none;
  }

  /deep/.ol-tooltip-measure {
    opacity: 1;
    font-weight: bold;
  }

  /deep/.ol-tooltip-static {
    background-color: #ffcc33;
    color: black;
    border: 1px solid white;
  }

  /deep/.ol-tooltip-measure:before,
  /deep/.ol-tooltip-static:before {
    border-top: 6px solid rgba(0, 0, 0, 0.5);
    border-right: 6px solid transparent;
    border-left: 6px solid transparent;
    content: "";
    position: absolute;
    bottom: -6px;
    margin-left: -7px;
    left: 50%;
  }

  /deep/.ol-tooltip-static:before {
    border-top-color: #ffcc33;
  }
</style>

然后就可以看到我们点击“开始测距”按钮之后,上面代码执行,鼠标旁边就出现一个小小的操作提示。

Vue OpenLayer测距功能的实现

鼠标绘制线

好的,通过上面的代码呢,我们成功的绘制出了提示框,然后就是鼠标绘制,代码也很简单,在map监听的pointermove方法中,继续创建一个draw进行绘制,关键代码就是下面:

draw = new Draw({
          source,  // 这个数据源就是我们最开始的那个数据源,这是简写,实际上是 source:source,
          type: 'LineString',  // 绘制线
          style: new Style({   // 绘制完成之前线的样式,这是官网的样式,需要的话自己可以修改成自己想要的样子
            fill: new Fill({
              color: 'rgba(255, 255, 255, 0.2)',
            }),
            stroke: new Stroke({
              color: 'rgba(0, 0, 0, 0.5)',
              lineDash: [10, 10],
              width: 4,
            }),
            image: new CircleStyle({
              radius: 5,
              stroke: new Stroke({
                color: 'rgba(0, 0, 0, 0.7)',
              }),
              fill: new Fill({
                color: 'rgba(255, 255, 255, 0.2)',
              }),
            }),
          }),
        });

然后把draw绑定到地图上面。

map.addInteraction(draw);   // draw 绑定到地图上面去

然后就实现了鼠标绘制线。

Vue OpenLayer测距功能的实现

设置距离信息窗

在我们点击开始测量的时候呢,在我们拖动鼠标的时候,会在上方显示出当前距离起点的距离,这个地方代码实现就是下面的样子,继续在上面的代码后面写:

// 开始监听绘制
        draw.on('drawstart', (evt) => {
          feature = evt.feature;  // feature就是全局的
          let tooltipCoord = evt.coordinate;  // 鼠标当前的位置 
          listener = feature.getGeometry().on('change', function (evt) {
            const geom = evt.target;
            let output = formatLength(geom);   // 距离的格式
            tooltipCoord = geom.getLastCoordinate();    // 设置鼠标位置改变后的实时位置
            measureTooltipElement.innerHTML = output;  // 设置提示框的内容,就是距离
            measureTooltip.setPosition(tooltipCoord);  // 设置距离提示框的位置
          });
        });
        
        // 格式化长度, 直接官网代码
        const formatLength = function (line) {
          const length = getLength(line);
          let output;
          if (length > 100) {
            output = Math.round((length / 1000) * 100) / 100 + ' ' + 'km';
          } else {
            output = Math.round(length * 100) / 100 + ' ' + 'm';
          }
          return output;
        };
        
		this.createMeasureTooltip()  // 创建那个距离的提示框

然后上面代码调用了一个方法。

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

完成上面的代码之后,我们在点击开始测量之后,会在鼠标上方实时显示当前鼠标位置距离起点的距离。

Vue OpenLayer测距功能的实现

绘制完成

上边已经实现了点击开始测距,并且实时显示距离信息,接下来就是双击完成时候显示出总长度。

继续在之前代码后边写:

// 双击绘制完成
        draw.on('drawend', () => {
          measureTooltipElement.className = 'ol-tooltip ol-tooltip-static';
          measureTooltip.setOffset([0, -7]);
          feature = null;
          measureTooltipElement = null;
          this.createMeasureTooltip();
          unByKey(listener);
        });

上边的代码基本上就是官网的代码,但是变量名不一样的地方需要稍微改一下。

通过上面的代码就实现了双击测量完成的功能。

Vue OpenLayer测距功能的实现

OK,到此为止,测距功能全部完成!

取消绘制

绘制功能完成了,就需要取消绘制,取消绘制需要在点击“取消绘制”按钮之后,取消地图绘制功能,删除界面上已经绘制过的内容。

首先我们需要删除地图上绘制过的内容,包括连线,以及弹窗。

这个地方需要注意一下,我们需要把绘制的图层,比如连线,和弹窗都保存到一个或者是几个列表里面,然后在点击按钮的时候,去遍历删除。

所以说我们要在点击测距时候加载到地图的图层之后,将创建的图层添加到一个数组存起来。

map.addLayer(layer)
this.drawLayers.push(layer)  // 保存起来

包括那个总距离的弹窗。

this.drawElements.push(measureTooltip)
map.addOverlay(measureTooltip);  // 保存起来

然后点击“取消测量”按钮的时候执行下面的代码:

// 取消绘制
      cancal() {
        for(let i = 0 ; i< this.drawLayers.length; i++) {
          map.removeLayer(this.drawLayers[i])
        }
        for (let i = 0; i < this.drawElements.length; i++) {
          map.removeOverlay(this.drawElements[i])
        }
        this.drawLayers = []
        this.drawElements = []
        map.removeInteraction(draw)
        unByKey(mapMouseMove);
      },

这样就可以了。

Vue OpenLayer测距功能的实现

这样就完成了!

全部代码

这里分享一下全部代码,就不放资源了,下载还花钱,我也是跟人家学的,没必要都。

<template>
  <div class="home">
    <div class="set">
      <button class="btn" @click="distance()">测距</button>
      <button class="btn" @click="cancal()">取消</button>
    </div>
    <div id="map" ref="map"></div>
  </div>
</template>

<script>
  import 'ol/ol.css';
  import Draw from 'ol/interaction/Draw';
  import Map from 'ol/Map';
  import Overlay from 'ol/Overlay';
  import View from 'ol/View';
  import { Circle as CircleStyle, Fill, Stroke, Style } from 'ol/style';
  import { LineString, Polygon } from 'ol/geom';
  import { OSM, Vector as VectorSource } from 'ol/source';
  import { Tile as TileLayer, Vector as VectorLayer } from 'ol/layer';
  import { getArea, getLength } from 'ol/sphere';
  import { unByKey } from 'ol/Observable';

  var map = null

  var helpTooltipElement = null
  var feature = null;
  var helpTooltip = null;
  var draw = null;
  var measureTooltipElement = null;
  var measureTooltip = null;
  var listener = null;
  var mapMouseMove = null;

  export default {
    name: "Home",
    data() {
      return {
        drawLayers: [],
        drawElements: [],
      }
    },
    mounted() {
      this.initMap()
    },
    methods: {
      // 初始化地图
      initMap() {
      
        map = new Map({
          layers: [
            new TileLayer({
              source: new OSM(),
            }),
          ],
          target: 'map',
          view: new View({
            center: [0, 0],
            zoom: 5,
            maxZoom: 18,
            // projection: 'EPSG:4326',
            constrainResolution: true,  // 设置缩放级别为整数 
            smoothResolutionConstraint: false,  // 关闭无级缩放地图
          }),
        });
      },


      // 测距
      distance() {

        let source = new VectorSource()
        const layer = new VectorLayer({
          source: source,
          style: new Style({
            fill: new Fill({
              color: 'rgba(255, 255, 255, 0.2)',
            }),
            stroke: new Stroke({
              color: '#ffcc33',
              width: 4,
            }),
            image: new CircleStyle({
              radius: 7,
              fill: new Fill({
                color: '#ffcc33',
              }),
            }),
          }),
        });

        mapMouseMove = map.on('pointermove', (ev) => {
          let helpMsg = '点击开始测量'
          if (feature) {
            helpMsg = '双击结束测量'
          }
          helpTooltipElement.innerHTML = helpMsg;
          helpTooltip.setPosition(ev.coordinate);
          helpTooltipElement.classList.remove('hidden')

        })

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

        draw = new Draw({
          source,
          type: 'LineString',
          style: new Style({
            fill: new Fill({
              color: 'rgba(255, 255, 255, 0.2)',
            }),
            stroke: new Stroke({
              color: 'rgba(0, 0, 0, 0.5)',
              lineDash: [10, 10],
              width: 4,
            }),
            image: new CircleStyle({
              radius: 5,
              stroke: new Stroke({
                color: 'rgba(0, 0, 0, 0.7)',
              }),
              fill: new Fill({
                color: 'rgba(255, 255, 255, 0.2)',
              }),
            }),
          }),
        });

        // 开始坚挺绘制
        draw.on('drawstart', (evt) => {
          feature = evt.feature;

          let tooltipCoord = evt.coordinate;

          listener = feature.getGeometry().on('change', function (evt) {
            const geom = evt.target;
            let output = formatLength(geom);
            tooltipCoord = geom.getLastCoordinate();
            measureTooltipElement.innerHTML = output;
            measureTooltip.setPosition(tooltipCoord);
          });
        });

        // 双击绘制完成
        draw.on('drawend', () => {
          measureTooltipElement.className = 'ol-tooltip ol-tooltip-static';
          measureTooltip.setOffset([0, -7]);
          feature = null;
          measureTooltipElement = null;
          this.createMeasureTooltip();
          unByKey(listener);
        });

        // 格式化长度
        const formatLength = function (line) {
          const length = getLength(line);
          let output;
          if (length > 100) {
            output = Math.round((length / 1000) * 100) / 100 + ' ' + 'km';
          } else {
            output = Math.round(length * 100) / 100 + ' ' + 'm';
          }
          return output;
        };

        this.createHelpTooltip()
        this.createMeasureTooltip()
        map.addLayer(layer)
        this.drawLayers.push(layer)
        map.addInteraction(draw);
      },

      // 取消绘制
      cancal() {
        for(let i = 0 ; i< this.drawLayers.length; i++) {
          map.removeLayer(this.drawLayers[i])
        }
        for (let i = 0; i < this.drawElements.length; i++) {
          map.removeOverlay(this.drawElements[i])
        }
        this.drawLayers = []
        this.drawElements = []
        map.removeInteraction(draw)
        unByKey(mapMouseMove);
      },

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

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

</script>
<style scoped>
  .home {
    width: 100%;
    height: 100%;
    background-color: aliceblue;
    position: relative;
  }

  #map {
    height: 100%;
    width: 100%;
  }

  .set {
    position: absolute;
    top: 0px;
    right: 0px;
    z-index: 99;
  }

  .btn {
    margin: 10px;
  }

  /deep/.hidden {
    display: none;
  }

  /deep/.ol-tooltip {
    position: relative;
    background: rgba(0, 0, 0, 0.5);
    border-radius: 4px;
    color: white;
    padding: 4px 8px;
    opacity: 0.7;
    white-space: nowrap;
    font-size: 12px;
    cursor: default;
    user-select: none;
  }

  /deep/.ol-tooltip-measure {
    opacity: 1;
    font-weight: bold;
  }

  /deep/.ol-tooltip-static {
    background-color: #ffcc33;
    color: black;
    border: 1px solid white;
  }

  /deep/.ol-tooltip-measure:before,
  /deep/.ol-tooltip-static:before {
    border-top: 6px solid rgba(0, 0, 0, 0.5);
    border-right: 6px solid transparent;
    border-left: 6px solid transparent;
    content: "";
    position: absolute;
    bottom: -6px;
    margin-left: -7px;
    left: 50%;
  }

  /deep/.ol-tooltip-static:before {
    border-top-color: #ffcc33;
  }
</style>

以上就是Vue+OpenLayer实现测距功能 的详细内容!

Vue.js 相关文章推荐
详解vue 组件注册
Nov 20 Vue.js
Vue组件生命周期运行原理解析
Nov 25 Vue.js
vue开发chrome插件,实现获取界面数据和保存到数据库功能
Dec 01 Vue.js
vue项目中企业微信使用js-sdk时config和agentConfig配置方式详解
Dec 15 Vue.js
vue组件是如何解析及渲染的?
Jan 13 Vue.js
如何管理Vue中的缓存页面
Feb 06 Vue.js
vue2实现provide inject传递响应式
May 21 Vue.js
使用Vue3+Vant组件实现App搜索历史记录功能(示例代码)
Jun 09 Vue.js
详解Vue router路由
Nov 20 Vue.js
Vue全局事件总线你了解吗
Feb 24 Vue.js
vue使用echarts实现折线图
Mar 21 Vue.js
vue中控制mock在开发环境使用,在生产环境禁用方式
Apr 06 Vue.js
vue 给数组添加新对象并赋值
Apr 20 #Vue.js
vue 数字翻牌器动态加载数据
Apr 20 #Vue.js
vue3.0 数字翻牌组件的使用方法详解
Apr 20 #Vue.js
vue封装数字翻牌器
Apr 20 #Vue.js
vue特效之翻牌动画
Apr 20 #Vue.js
解决vue中provide inject的响应式监听
Apr 19 #Vue.js
vue3种table表格选项个数的控制方法
Apr 14 #Vue.js
You might like
PHP高级编程实例:编写守护进程
2014/09/02 PHP
php类中的各种拦截器用法分析
2014/11/03 PHP
php抽象类使用要点与注意事项分析
2015/02/09 PHP
PHP PDO和消息队列的个人理解与应用实例分析
2019/11/25 PHP
Jquery 获取checkbox的checked问题
2011/11/16 Javascript
jQuery实现鼠标可拖动调整表格列宽度
2014/05/26 Javascript
JavaScript设计模式之原型模式(Object.create与prototype)介绍
2014/12/28 Javascript
js实现从数组里随机获取元素
2015/01/12 Javascript
js+html5通过canvas指定开始和结束点绘制线条的方法
2015/06/05 Javascript
Javascript 是你的高阶函数(高级应用)
2015/06/15 Javascript
一起学写js Calender日历控件
2016/04/14 Javascript
JavaScript基于扩展String实现替换字符串中index处字符的方法
2017/06/13 Javascript
详解jQuery中关于Ajax的几个常用的函数
2017/07/17 jQuery
React Native中TabBarIOS的简单使用方法示例
2017/10/13 Javascript
浅谈Angular 中何时取消订阅
2017/11/22 Javascript
bootstrap动态调用select下拉框的实例代码
2018/08/09 Javascript
vue实现PC端录音功能的实例代码
2019/06/05 Javascript
微信小程序-可移动菜单的实现过程详解
2019/06/24 Javascript
对Layer UI 模块化的用法详解
2019/09/26 Javascript
JavaScript函数IIFE使用详解
2019/10/21 Javascript
Vue组件跨层级获取组件操作
2020/07/27 Javascript
ant design中upload组件上传大文件,显示进度条进度的实例
2020/10/29 Javascript
javascript 数组(list)添加/删除的实现
2020/12/17 Javascript
[04:54]DOTA2-DPC中国联赛1月31日Recap集锦
2021/03/11 DOTA
介绍Python中的fabs()方法的使用
2015/05/14 Python
Python中Django框架利用url来控制登录的方法
2015/07/25 Python
Python正则表达式如何进行字符串替换实例
2016/12/28 Python
pandas和spark dataframe互相转换实例详解
2020/02/18 Python
双立人美国官方商店:ZWILLING集团餐具和炊具
2020/05/07 全球购物
学期自我鉴定范文
2013/10/01 职场文书
外科实习自我鉴定
2013/10/06 职场文书
幼儿园新学期寄语
2014/01/18 职场文书
30岁生日感言
2014/01/25 职场文书
先进个人事迹材料范文
2014/12/30 职场文书
实习科室评语
2015/01/04 职场文书
Python 数据可视化之Bokeh详解
2021/11/02 Python