OpenLayer基于vue的封装使用教程

目录
  • 前言
  • 基本结构
    • _createView
    • _createBaselayer
    • _createMap
    • _initMapEvt
    • _initMapControl
    • _initMapClickEvent()
    • _initPointMoveEvent

openlayer是目前我们gis常用的一款开源的,并且反馈都特别好的软件了,像之前的ol3, 风靡一时,地图实现也很简单,很实用,目前vue中使用地图也是非常多的,那么如果在vue中引入openlayer并且实现地图撒点效果,甚至是更深层的地图聚合效果呢,本文来分享下OpenLayer基于vue的封装使用,感兴趣的朋友一起看看吧!

前言

公司项目使用了openlayer作为2d平面地图来使用,之前没有接触过,开一篇文章记录一下。顺便捋一下代码里面封装的结构。

基本结构

openlayer使用的版本是"^6.4.3",引入了mapbox的样式,"ol-mapbox-style": "^8.2.0"。地图的初始化专门封装了一个class类,用于初始化地图使用。

import Object from 'ol/Object'
import View from 'ol/View'
import Map from 'ol/Map'

class EMap extends Object {
  constructor (options) {
    super(options)
    this.options = assignObj({}, options)
    this._view = undefined
    this._baseLayers = []
    this._map = undefined
    this.vectorLayers = []
    this.rasterLayers = []
    this.controls = []
    this._mapClickFunc = options.mapclickFunction
    this._mapEvtBus = options.mapEvtBus

    this._interactionsState = {}
    this._initMap()
  }
}

assignObj方法是Object.assign方法,但是刚好ol自己有一个Object类,避免冲突就需要更改一下这个方法名了。

主要结构有这几种:map地图,view视图,layer图层,controls控制器,mapClickFunc地图相关的点击事件,mapEvtBus地图事件总线。

_initMap()方法用来初始化地图。方法代码内容如下:

  _initMap () {
    this._view = this._createView()
    this._baseLayers = this._createBaseLayer()
    this._map = this._createMap()
    this._initMapEvt()
  }

_createView

_createView()方法用来初始化view视图。方法代码内容如下:

import {get as getProject} from 'ol/proj'
 _createView () {
    let viewOptions = assignObj( this._getDefaultViewOptions(), this.options.view)
    if (!viewOptions.projectionCode) {
      viewOptions.projection = 'EPSG:3857'
    } else {
      viewOptions.projection = `EPSG:${viewOptions.projectionCode}`
    }
    delete viewOptions.projectionCode

    // let projection = getProject(viewOptions.projection)
    // if (!projection) {
    //  projection = getProject('EPSG:4326')
    // }
    // let projectionExtent = projection.getExtent()
    // let width = getWidth(projectionExtent)
    // let resolutions = []
    // for (let z = 0; z < 25; z++) {
    //   resolutions[z] = width / (256 * Math.pow(2, z))
    // }

    // console.log('分辨率1', resolutions)

    // viewOptions.resolutions = resolutions
    let view = new View(viewOptions)
    return view
  }

首先通过_getDefaultViewOptions方法,获取view的一些默认配置,然后将传入的options的配置使用assign方法进行合并。

然后就是判断坐标系编码,这个判断逻辑可以根据需要来更改,ol默认的坐标系就是3857,在官网中有说明。

注释掉的代码,是对分辨率进行的处理,根据需要可以自行添加进去。

_getDefaultViewOptions()方法存储一些默认配置,比如中心点,坐标系,缩放这种。

_getDefaultViewOptions() {
  let options = {
    projectionCode: '3857',
    center: [120, 69],
    zoom: 5
  }
  return options
}

如果地图的配置项是通过接口获取数据,那默认配置最好和接口返回的数据对应,这样即使接口中有某个数据没法通过校验,就可以使用默认值了。校验方法放在_createView中和默认配置分开,逻辑会清晰点,不会挤在同一个方法里面。

_createBaselayer

_createBaselayer()方法主要是创建底图,底图可能是天地图,mapbox,高德,百度等,因此不同的底图执行的代码逻辑是不一样的,需要加判断分别处理。

  _createBaseLayer () {
    const baseLayerOptions = this.options.baseLayer
    if (!baseLayerOptions.type ) {
      baseLayerOptions.type = 'mapbox'
    }

    if (baseLayerOptions.type === 'tianditu') {
      return this._createTianDiTuLayers(baseLayerOptions)
    } else if (baseLayerOptions.type === 'mapbox') {
      return this._createMapBoxLayers(baseLayerOptions)
    } else {
      return this._createXYZLayer(baseLayerOptions)
    }
  }

以处理天地图_createTianDiTuLayers为例,通过接口请求到的底图参数中有一个baseLayer属性,存储一个对象,除了携带type属性外,还有对应的token信息。

import {createXYZ} from 'ol/tilegrid'
import Tile from 'ol/layer/Tile'
import XYZ from 'ol/source/XYZ'

_createTianDiTuLayers() {
    const tdtToken = baseLayerOptions.tdtToken
    const baseURL = 'http://t{0-7}.tianditu.gov.cn/'
    const layerOptions = [
      {
        title: '天地图矢量',
        layerName: 'vec_c',
        attributions: '右下角署名',
        visible: true,
        type: 'vec'
      },
      {
        title: '天地图矢量注记',
        layerName: 'cva_c',
        attributions: '',
        visible: true,
        type: 'vec'
      },
      {
        title: '天地图卫星影像',
        layerName: 'img_c',
        attributions: '右下角署名',
        visible: false,
        type: 'img'
      },
      {
        title: '天地图卫星影像注记',
        layerName: 'cia_c',
        attributions: '',
        visible: false,
        type: 'img'
      },
    ]
}

底图可以是多个图层叠加的,因此baseLayers是一个数组。layerOptions存储了一些天地图的信息,通过visible设置是否启用,一般是矢量图或者图片加上对应的标注。

    var projection = new getProject('EPSG:3857')

    let tilegrid = createXYZ({
      extent: projection.getExtent()
    })

    const layers = layerOptions.map((item) => {
      let layerType = item.layerName.split('_')
      const url = `${baseURL}${item.layerName}/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=${layerType[0]}&STYLE=default&TILEMATRIXSET=${layerType[1]}&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=${tdtToken}`
      const attributions = item.attributions === '' ? undefined : ` <a href="http://www.baidu.com" target="_blank">${item.attributions}</a>`
      const layer = new Tile({
        title: item.title,
        source: new XYZ({
          attributions: attributions,
          url: url,
          wrapx: false,
          crossOrigin: 'anonymous',
          projection: projection,
          tileGrid: tilegrid
        }),
        minZoom: 0,
        maxZoom: 20
      })
      layer.setProperties({
        layerType: item.type,
        isBaseLayer: true
      })
      layer.setVisible(item.visible)
      return layer
    })

最主要的内容还是layer,使用ol/layer/Tile设置标题,数据源,最大最小缩放,tileGrid根据坐标系设置范围。openlayer的图层添加后,会在右下角有一个感叹号,里面的内容就是由source的attributions来定义的。crossOrigin是设置canvas的跨域属性。mdn对它有解释,它有三种值可以设置。

是h5的特性支持,和openlayer无关就是了。

为layer设置了两个值,这两个值本身是没有的,用setProperties添加进去。后续可以使用getProperties()来获取这两个值。根据设置好的visible设置layer的可见性。这样关于天地图的底图设置逻辑就完成了。

_createMap

_createMap()方法创建map地图,添加一些控件,代码中添加了一个比例尺

import ScaleLine from 'ol/control/ScaleLine'
import { defaults } from 'ol/control'

  _createMap () {
    const map = new Map({
      target: this.options.target,
      view: this._view,
      layers: this._baseLayers,
      controls: new defaults({
        attribution: true,
        attributionOptions: {
          tipLabel: '信息'
        },
        zoomOptions: {
          zoomInTipLabel: '放大',
          zoomOutTipLabel: '缩小',
        }
      })
    })
    const scale = new ScaleLine({
      bar: true,
      text: true,
      minWidth: 125
    })
    map.addControl(scale)
    return map
  }

_initMapEvt

_initMapEvt()处理地图的一些控制和交互功能。

  _initMapEvt () {
    this._initMapControl()
    this._initMapClickEvent()
    this._initPointMoveEvent()
  }

_initMapControl

_initMapControl()方法主要是去除一些容易和后面的操作冲突的事件。

import DoubleClickZoom from 'ol/interaction/DoubleClickZoom'

  _initMapControl () {
    // 移除双击缩放控件(与双击弹属性窗冲突)
    let controls = this._map.getInteractions()
    let dbClickZoomControl = controls.getArray().find((control) => control instanceof DoubleClickZoom)
    if(dbClickZoomControl) {
      this._map.removeInteraction(dbClickZoomControl)
    }

    this._singleClickControl = new Select({
      condition: function (evt) {
        return evt.type === 'singleclick' || evt.type === 'dblclick'
      },
      // style: this._singleClickStyle.bind(this), // 如果需要自定义每个图层的选中样式,请开启这个属性
      layers: function (layer) {
        const layerName = layer.rootLayerName
        return this.findLayer(this.vectorLayers, layerName)
      }.bind(this)
    })

    var selectedFeatures = this._singleClickControl.getFeatures()
    selectedFeatures.on(['add','remove'], (evt) => {
      this.dispatchEvent({
        type: 'selectDataChanged',
        target: selectedFeatures,
        element: evt.element,
        option: evt.type
      })
    })

    if(this._map) {
      this._map.addInteraction(this._singleClickControl)
    }
  }

使用getInteractions()获取到所有交互,用类型检测出双击事件,然后移除。再加入自定义的singleClickControl,在add和remove的时候触发。

_initMapClickEvent()

  _initMapClickEvent () {
    this._map.on('click', (evt) => {
      // 单击事件优先选择控件中的单击选中事件
      const features = this._map.getFeaturesAtPixel(evt.pixel)
      if(features.length > 0) {
        features.forEach((ft) => {
          // map上的单击事件和layer的单击事件,取其一,优先map
          if(this._mapClickFunc) {
            this._mapClickFunc({
              data: ft,
              evt: evt
            })
          } else {
            const layerName = ft.get('layerName')
            const eLayer = this.findLayer(this.vectorLayers, layerName)
            if(eLayer) {
              eLayer._singleClick(ft, evt)
            }
          }
        })
      } else {
        if(this._mapClickFunc) {
          this._mapClickFunc({
            data: undefined,
            evt: evt
          })
        }
      }
    })

    this._map.on('dblclick', (evt) => {
      const features = this._map.getFeaturesAtPixel(evt.pixel)
      if (features.length > 0) {
        features.forEach((ft) => {
          const layerName = ft.get('layerName')
          const eLayer = this.findLayer(this.vectorLayers, layerName)
          if(eLayer) {
            eLayer._dbClick(ft, evt)
          }
        })
      }
    })
  }

_initMapClickEvent()主要处理单击和双击事件,后续加入进去的layer图层可以自己定义单击事件。初始化map对象的时候,也可以自己传入mapClickFunc。代码中优先取map的单击事件。

findLayer方法为自定义方法,主要是通过layername拿到对应的layer。

_initPointMoveEvent

  _initPointMoveEvent () {
    this._map.on('pointermove', (evt) => {
      const features = this._map.getFeaturesAtPixel(evt.pixel)
      if(features.length > 0) {
        this._map.getTargetElement().style.cursor = 'pointer'
      } else {
        this._map.getTargetElement().style.cursor = 'auto'
      }
    })
  }

_initPointMoveEvent()方法,当鼠标移动到某个features上时候,鼠标形状改变。用来告诉用户,鼠标位置存在可以交互的东西。

然后就是一些普通的getter和setter方法。可以按自己喜好多封装一些常用的。

  getOlMap () {
    return this._map
  }

  getView () {
    return this._view
  }

  getMapProjection () {
    return this.getView().getProjection()
  }

  getZoom () {
    if(this._view) {
      return this._view.getZoom()
    }
  }

  getBaseLayers () {
    return this._baseLayers
  }

  setZoom (zoom) {
    if (this._view) {
      this._view.setZoom(zoom)
    }
  }

  setCenter (point) {
    this._view.setCenter(point)
  }

  setView (view) {
    this._map.setView(view)
    this._view = view
  }

  zoomToNext () {
    let zoom = this.getZoom()
    zoom = parseInt(zoom)
    this.setZoom(zoom + 1)
  }

  fit (geom) {
    this._view.fit(geom)
  }

  fitToLayer (eLayer) {
    if(eLayer.getDataExtent) {
      const extent = eLayer.getDataExtent()
      const resolution = this._view.getResolution()
      // 范围缩小一点,要不然碰到地图边界
      extent[0] = extent[0] - 1 * resolution
      extent[1] = extent[1] - 1 * resolution
      extent[2] = extent[2] + 1 * resolution
      extent[3] = extent[3] + 1 * resolution
      if (extent) {
        this.fit(extent)
      }
    }
  }

  zoomToPrevious () {
    let zoom = this.getZoom()
    zoom = parseInt(zoom)
    this.setZoom(zoom - 1)
  }

  getExtent () {
    return this._view.calculateExtent(this._map.getSize())
  }

地图的初始化操作就这么多,接下来就是一些layer图层上面的添加,查找,移除的操作。

import _ from 'lodash'

  addLayer (eLayer) {
    const layer = eLayer.getLayer()
    if (layer) {
      if (eLayer.get('eLayerType') === layerDataType.vector) {
        if (!this.findLayer(this.vectorLayers, eLayer.get('layerName'))) {
          this.vectorLayers.push(eLayer)
          this._map.addLayer(layer)
        } else {
          console.log('layer is exist')
        }
      } else if (eLayer.get('eLayerType') === layerDataType.raster ) {
        if (!this.findLayer(this.rasterLayers, eLayer.get('layerName'))) {
          this.rasterLayers.push(eLayer)
          this._map.addLayer(layer)
        } else {
          console.log('layer is exist')
        }
      } else {
        console.log('layer is not eMapLayer...')
      }
    }
  }

  findLayer (layerList, layerName) {
    if (layerList) {
      const layer = _.find(layerList, (layer) => {
        return layer.get('layerName') === layerName
      })
      return layer
    }
    return null
  }

  removeLayer (eLayer) {
    const layer = eLayer.getLayer()
    if (layer) {
      if (eLayer.get('eLayerType') === layerDataType.vector) {
        _.remove(this.vectorLayers, (layer) => {
          return layer === eLayer
        })
        this._map.removeLayer(layer)
      } else if(eLayer.get('eLayerType') === layerDataType.raster) {
        _.remove(this.rasterLayers, (layer) => {
          return layer === eLayer
        })
        this._map.removeLayer(layer)
      } else {
        console.log('layer is not eMapLayer...')
      }
    }
  }

  removeLayerByName (layerName) {
    let eLayer = this.findLayer(this.vectorLayers, layerName)
    if (eLayer) {
      this.removeLayer(eLayer)
    } else {
      eLayer = this.findLayer(this.rasterLayers, layerName)
      if (eLayer) {
        this.removeLayer(eLayer)
      }
    }
  }

后面layer图层也会进行一次封装,有一个eLayerType的字符串值,决定是放在哪个图层数组里面。名称不能重复,如果检测到重复名称说明图层已经添加过了,就不会重新添加了。

当存在一些编辑功能的时候,防止冲突,就要停止和恢复一些交互功能。封装两个方法。

  /**
   * 暂停作用域以外的交互控件(默认不暂停)
   * @param {string}} scope
   */
  pauseInteraction (scope) {
    let interactions = this._map.getInteractions()
    interactions.forEach((itc) => {
      if(!itc.rootName) {
        return
      }
      if(itc.rootName !== scope) {
        let id = itc.ol_uid
        this._interactionsState[id] = itc.getActive()
        itc.setActive(false)
      }
    })
  }

  resumeInteraction () {
    let interactions = this._map.getInteractions()
    interactions.forEach((itc) => {
      if(itc.rootName) {
        let id = itc.ol_uid
        let active = this._interactionsState[id]
        if(active) {
          itc.setActive(active)
        }
      }
    })
  }

单击显示的数据

  showDetail (data, zoom, point, id, geomType) {
    if (this._mapEvtBus) {
      const options = {
        data,
        zoom,
        point,
        id,
        geomType
      }
      this._mapEvtBus.$emit('showDetail', options)
    }
  }

到此这篇关于OpenLayer基于vue的封装使用的文章就介绍到这了,更多相关vue OpenLayer内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • vue使用openlayers实现移动点动画

    本文实例为大家分享了vue使用openlayers实现移动点动画的具体代码,供大家参考,具体内容如下 做项目时,本来打算仿照官网的Example中动画制作,引入vue中后,发现它引用的库函数一直报错,最后我去vue中安装的依赖库中去查找这个函数,果然没有.也就是说官方例子使用的库和我安装的OL库存在一定差异. 后来我还是用笨方法去解决了,最终效果如下: 总体思路是将移动目标实例一个Overlay对象,然后将如图5个经纬度点没两点之间分割成多个(200个),之后通过定时器不断setPositon.

  • vue利用openlayers加载天地图和高德地图

    目录 一.天地图部分 1.在vue中安装openlayers 二.高德地图部分 一.天地图部分 1.在vue中安装openlayers npm i --save ol 这里说的vue是基于脚手架构建的. 新建个页面,也就是vue文件,配置好路由.接着就是可以直接放入我的代码运行显示了. <template> <div class="wrapper"> <div>天地图</div> <div class="map"

  • Vue+Openlayers自定义轨迹动画

    本文实例为大家分享了Vue+Openlayers实现轨迹动画的具体代码,供大家参考,具体内容如下 <template> <div class="map-warp"> <h3> <a href="https://openlayers.org/en/latest/examples/feature-move-animation.html?q=polyline" target="_bank" >Openla

  • vue项目中openlayers绘制行政区划

    vue项目中openlayers画行政区划(区域范围),供大家参考,具体内容如下 原理 在地图上画需要的范围,实际上就是在地图上打上一圈点,然后依次将这些点用线连接,就形成了范围 引用相应的ol模块 import VectorLayer from 'ol/layer/Vector' import VectorSource from 'ol/source/Vector' import { Map, View, Feature } from 'ol' import { Style, Icon, St

  • vue集成openlayers加载geojson并实现点击弹窗教程

    本文实例为大家分享了vue+openlayers加载geojson并实现点击弹窗教程,供大家参考,具体内容如下 第一步:安装vue-cli cnpm install -g @vue/cli 第二步:新建一个项目 1.新建项目 (vue-openlayers为项目名),并选择default模版 vue create vue-openlayers 2.安装openlayers cnpm i -S ol 第三步:写业务代码 1.删除掉HelloWorld.vue 新建 olmap.vue组件 comp

  • vue使用openlayers创建地图

    vue项目中使用openlayers创建地图,供大家参考,具体内容如下 前期准备 安装node环境 安装cnpm 安装vue-cli 以上步骤网上都有很多教程 搭建vue项目 vue create vue-ol 按照提示一步步搭建vue项目 cd vue-ol npm run serve 浏览器打开 http://localhost:8080/ 就可以看到初始化的vue项目页面 vue项目安装openlayers cnpm i ol --s main.js中引入ol.css import 'ol

  • vue-openlayers实现地图坐标弹框效果

    本文实例为大家分享了vue-openlayers实现地图坐标弹框的具体代码,供大家参考,具体内容如下 openlayers 这个效果是点击地图,弹出坐标信息. 点击地图边缘时,底图会跟着移动,使弹窗能完整显示出来. <template> <div class="vm"> <h2 class="h-title">弹窗 popup</h2> <div id="map" class="ma

  • vue+openlayers绘制省市边界线

    本文实例为大家分享了vue+openlayers绘制省市边界线的具体代码,供大家参考,具体内容如下 1.创建项目 vue init webpack ol_vue 2.安装ol依赖包 npm install ol 3.引入axios npm install axios --save 文件目录:src/main.js import Vue from 'vue' import router from './router' import App from './App' import axios fro

  • OpenLayer基于vue的封装使用教程

    目录 前言 基本结构 _createView _createBaselayer _createMap _initMapEvt _initMapControl _initMapClickEvent() _initPointMoveEvent openlayer是目前我们gis常用的一款开源的,并且反馈都特别好的软件了,像之前的ol3, 风靡一时,地图实现也很简单,很实用,目前vue中使用地图也是非常多的,那么如果在vue中引入openlayer并且实现地图撒点效果,甚至是更深层的地图聚合效果呢,本

  • 基于Vue如何封装分页组件

    使用Vue做双向绑定的时候,可能经常会用到分页功能 接下来我们来封装一个分页组件 先定义样式文件 pagination.css ul, li { margin: 0px; padding: 0px; } .page-bar { -webkit-touch-callout: none; -webkit-user-select: none; -khtml-user-select: none; -moz-user-select: none; -ms-user-select: none; user-se

  • 在vue中封装可复用的组件方法

    本次封装的组件以toast组件为例 以前使用移动端ui插件时,通过一句代码比如 $.toast( ' 需要显示的内容 ' ),从而在页面上展示这段文字,并在一定时间后消失. 现在我们也尝试自己封装toast组件. 准备工作:vue-cli脚手架工程 先看一下涉及到的文件目录截图: 这次的封装主要涉及的文件是Toast.vue toast.js Hello.vue,主要思路如下: ① Toast.vue是我们要使用的toast组件: ② toast.js里面用Vue.extend()扩展一个组件构

  • 基于cropper.js封装vue实现在线图片裁剪组件功能

    效果图如下所示, github:demo下载 cropper.js github:cropper.js 官网(demo) cropper.js 安装 npm或bower安装 npm install cropper # or bower install cropper clone下载:下载地址 git clone https://github.com/fengyuanchen/cropper.git 引用cropper.js 主要引用cropper.js跟cropper.css两个文件 <scri

  • Vue 组件封装 并使用 NPM 发布的教程

    正文开始 Vue 开发插件 我们可以先查看Vue的插件的开发规范 我们开发的之后期望的结果是支持 import.require 或者直接使用 script 标签的形式引入,就像这样: ps: 这里注意一下包的名字前缀是 unisoft ,组件的名字前缀是 uni import UniSoftUI from 'unisoft-ui'; // 或者 const CustomUI = require('unisoft-ui'); // 或者 <script src="...">&

  • vue 全局封装loading加载教程(全局监听)

    前言: 为了页面美观,请求接口的时候延迟没有数据,页面感觉狠卡顿,封装loading,请求接口成功后隐藏掉(我这是用的vant 组件根据自己情况进行改变). 第一步: 建立loading.vue <template> <div class="loading"> <van-loading size="36px" vertical>加载中...</van-loading> </div> </templat

  • 基于axios请求封装的vue应用实例代码

    什么是axios? Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中. 特性: 从浏览器中创建 XMLHttpRequests 从 node.js 创建 http 请求 支持 Promise API 拦截请求和响应 转换请求数据和响应数据 取消请求 自动转换 JSON 数据 客户端支持防御 XSRF Promises axios 依赖原生的 ES6 Promise 实现而被支持. 如果你的环境不支持 ES6 Promise,你可以使用 polyfil

  • 基于vue封装下拉刷新上拉加载组件

    基于vue和原生javascript封装的下拉刷新上拉加载组件,供大家参考,具体内容如下 upTilte插槽是下拉刷新的自定义内容放的地方 downTilte插槽是上拉加载的自定义内容放的地方 默认插槽为列表内容区域 组件代码如下 <template> <div class="refresh" id="refresh"> <slot name="upTilte"></slot> <slot&g

  • 基于vue,vue-router, vuex及addRoutes进行权限控制问题

    基于vuex, vue-router,vuex的权限控制教程,完整代码地址见 https://github.com/linrunzheng/vue-permission-control 接下来让我们模拟一个普通用户打开网站的过程,一步一步的走完整个流程. 首先从打开本地的服务localhost:8080开始,我们知道打开后会进入login页面,那么判断的依据是什么. 首先是token. 没有登陆的用户是获取不到token的,而登陆后的角色我们会将token存到local或者seesionStor

  • 基于vue的fullpage.js单页滚动插件

    基于vue的fullpage.js使用方法,供大家参考,具体内容如下 功能概述 可实现移动端的单页滚动效果,支持横向滚动和纵向滚动 兼容性 目前还未进行大规模兼容性测试.有bug请提问至issues 安装 npm install vue-fullpage --save commonjs import VueFullpage from 'vue-fullpage' Vue.use(VueFullpage) 或 var vueFullpage = require('vue-fullpage') Vu

随机推荐