OpenLayer基于vue的封裝使用教程
openlayer是目前我們gis常用的一款開(kāi)源的,并且反饋都特別好的軟件了,像之前的ol3, 風(fēng)靡一時(shí),地圖實(shí)現(xiàn)也很簡(jiǎn)單,很實(shí)用,目前vue中使用地圖也是非常多的,那么如果在vue中引入openlayer并且實(shí)現(xiàn)地圖撒點(diǎn)效果,甚至是更深層的地圖聚合效果呢,本文來(lái)分享下OpenLayer基于vue的封裝使用,感興趣的朋友一起看看吧!
前言
公司項(xiàng)目使用了openlayer作為2d平面地圖來(lái)使用,之前沒(méi)有接觸過(guò),開(kāi)一篇文章記錄一下。順便捋一下代碼里面封裝的結(jié)構(gòu)。
基本結(jié)構(gòu)
openlayer使用的版本是"^6.4.3",引入了mapbox的樣式,"ol-mapbox-style": "^8.2.0"。地圖的初始化專(zhuān)門(mén)封裝了一個(gè)class類(lèi),用于初始化地圖使用。
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自己有一個(gè)Object類(lèi),避免沖突就需要更改一下這個(gè)方法名了。
主要結(jié)構(gòu)有這幾種:map地圖,view視圖,layer圖層,controls控制器,mapClickFunc地圖相關(guān)的點(diǎn)擊事件,mapEvtBus地圖事件總線(xiàn)。
_initMap()方法用來(lái)初始化地圖。方法代碼內(nèi)容如下:
_initMap () {
this._view = this._createView()
this._baseLayers = this._createBaseLayer()
this._map = this._createMap()
this._initMapEvt()
}_createView
_createView()方法用來(lái)初始化view視圖。方法代碼內(nèi)容如下:
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
}首先通過(guò)_getDefaultViewOptions方法,獲取view的一些默認(rèn)配置,然后將傳入的options的配置使用assign方法進(jìn)行合并。
然后就是判斷坐標(biāo)系編碼,這個(gè)判斷邏輯可以根據(jù)需要來(lái)更改,ol默認(rèn)的坐標(biāo)系就是3857,在官網(wǎng)中有說(shuō)明。

注釋掉的代碼,是對(duì)分辨率進(jìn)行的處理,根據(jù)需要可以自行添加進(jìn)去。
_getDefaultViewOptions()方法存儲(chǔ)一些默認(rèn)配置,比如中心點(diǎn),坐標(biāo)系,縮放這種。
_getDefaultViewOptions() {
let options = {
projectionCode: '3857',
center: [120, 69],
zoom: 5
}
return options
}如果地圖的配置項(xiàng)是通過(guò)接口獲取數(shù)據(jù),那默認(rèn)配置最好和接口返回的數(shù)據(jù)對(duì)應(yīng),這樣即使接口中有某個(gè)數(shù)據(jù)沒(méi)法通過(guò)校驗(yàn),就可以使用默認(rèn)值了。校驗(yàn)方法放在_createView中和默認(rèn)配置分開(kāi),邏輯會(huì)清晰點(diǎn),不會(huì)擠在同一個(gè)方法里面。
_createBaselayer
_createBaselayer()方法主要是創(chuàng)建底圖,底圖可能是天地圖,mapbox,高德,百度等,因此不同的底圖執(zhí)行的代碼邏輯是不一樣的,需要加判斷分別處理。
_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為例,通過(guò)接口請(qǐng)求到的底圖參數(shù)中有一個(gè)baseLayer屬性,存儲(chǔ)一個(gè)對(duì)象,除了攜帶type屬性外,還有對(duì)應(yīng)的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: '天地圖衛(wèi)星影像',
layerName: 'img_c',
attributions: '右下角署名',
visible: false,
type: 'img'
},
{
title: '天地圖衛(wèi)星影像注記',
layerName: 'cia_c',
attributions: '',
visible: false,
type: 'img'
},
]
}底圖可以是多個(gè)圖層疊加的,因此baseLayers是一個(gè)數(shù)組。layerOptions存儲(chǔ)了一些天地圖的信息,通過(guò)visible設(shè)置是否啟用,一般是矢量圖或者圖片加上對(duì)應(yīng)的標(biāo)注。
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 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
})最主要的內(nèi)容還是layer,使用ol/layer/Tile設(shè)置標(biāo)題,數(shù)據(jù)源,最大最小縮放,tileGrid根據(jù)坐標(biāo)系設(shè)置范圍。openlayer的圖層添加后,會(huì)在右下角有一個(gè)感嘆號(hào),里面的內(nèi)容就是由source的attributions來(lái)定義的。crossOrigin是設(shè)置canvas的跨域?qū)傩浴dn對(duì)它有解釋?zhuān)腥N值可以設(shè)置。

是h5的特性支持,和openlayer無(wú)關(guān)就是了。
為layer設(shè)置了兩個(gè)值,這兩個(gè)值本身是沒(méi)有的,用setProperties添加進(jìn)去。后續(xù)可以使用getProperties()來(lái)獲取這兩個(gè)值。根據(jù)設(shè)置好的visible設(shè)置layer的可見(jiàn)性。這樣關(guān)于天地圖的底圖設(shè)置邏輯就完成了。
_createMap
_createMap()方法創(chuàng)建map地圖,添加一些控件,代碼中添加了一個(gè)比例尺
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), // 如果需要自定義每個(gè)圖層的選中樣式,請(qǐng)開(kāi)啟這個(gè)屬性
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()獲取到所有交互,用類(lèi)型檢測(cè)出雙擊事件,然后移除。再加入自定義的singleClickControl,在add和remove的時(shí)候觸發(fā)。
_initMapClickEvent()
_initMapClickEvent () {
this._map.on('click', (evt) => {
// 單擊事件優(yōu)先選擇控件中的單擊選中事件
const features = this._map.getFeaturesAtPixel(evt.pixel)
if(features.length > 0) {
features.forEach((ft) => {
// map上的單擊事件和layer的單擊事件,取其一,優(yōu)先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()主要處理單擊和雙擊事件,后續(xù)加入進(jìn)去的layer圖層可以自己定義單擊事件。初始化map對(duì)象的時(shí)候,也可以自己傳入mapClickFunc。代碼中優(yōu)先取map的單擊事件。
findLayer方法為自定義方法,主要是通過(guò)layername拿到對(duì)應(yīng)的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()方法,當(dāng)鼠標(biāo)移動(dòng)到某個(gè)features上時(shí)候,鼠標(biāo)形狀改變。用來(lái)告訴用戶(hù),鼠標(biāo)位置存在可以交互的東西。
然后就是一些普通的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()
// 范圍縮小一點(diǎn),要不然碰到地圖邊界
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())
}地圖的初始化操作就這么多,接下來(lái)就是一些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圖層也會(huì)進(jìn)行一次封裝,有一個(gè)eLayerType的字符串值,決定是放在哪個(gè)圖層數(shù)組里面。名稱(chēng)不能重復(fù),如果檢測(cè)到重復(fù)名稱(chēng)說(shuō)明圖層已經(jīng)添加過(guò)了,就不會(huì)重新添加了。
當(dāng)存在一些編輯功能的時(shí)候,防止沖突,就要停止和恢復(fù)一些交互功能。封裝兩個(gè)方法。
/**
* 暫停作用域以外的交互控件(默認(rèn)不暫停)
* @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)
}
}
})
}單擊顯示的數(shù)據(jù)
showDetail (data, zoom, point, id, geomType) {
if (this._mapEvtBus) {
const options = {
data,
zoom,
point,
id,
geomType
}
this._mapEvtBus.$emit('showDetail', options)
}
}
到此這篇關(guān)于OpenLayer基于vue的封裝使用的文章就介紹到這了,更多相關(guān)vue OpenLayer內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue實(shí)現(xiàn)驗(yàn)證碼倒計(jì)時(shí)按鈕
這篇文章主要為大家詳細(xì)介紹了vue實(shí)現(xiàn)驗(yàn)證碼倒計(jì)時(shí)按鈕,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-08-08
vue項(xiàng)目動(dòng)態(tài)渲染input,綁定的參數(shù)不實(shí)時(shí)更新問(wèn)題
這篇文章主要介紹了vue項(xiàng)目動(dòng)態(tài)渲染input,綁定的參數(shù)不實(shí)時(shí)更新問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-03-03
解決vue中修改export default中腳本報(bào)一大堆錯(cuò)的問(wèn)題
今天小編就為大家分享一篇解決vue中修改export default中腳本報(bào)一大堆錯(cuò)的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-08-08
vue中設(shè)置echarts寬度自適應(yīng)的代碼步驟
這篇文章主要介紹了vue中設(shè)置echarts寬度自適應(yīng)的問(wèn)題及解決方案,常常需要做到echarts圖表的自適應(yīng),一般是根據(jù)頁(yè)面的寬度做對(duì)應(yīng)的適應(yīng),本文記錄一下設(shè)置echarts圖表的自適應(yīng)的步驟,需要的朋友可以參考下2022-09-09
VUE 文字轉(zhuǎn)語(yǔ)音播放的實(shí)現(xiàn)示例
本文主要介紹了VUE 文字轉(zhuǎn)語(yǔ)音播放的實(shí)現(xiàn)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-02-02

