Vue+OpenLayer實(shí)現(xiàn)測距功能
前言
首先呢說明一下,我是跟著一個大佬學(xué)的,所以我是個小次佬,openlayer的官網(wǎng)上面給出了案例,但是習(xí)慣vue開發(fā)的我完全不理解,關(guān)鍵是連注釋都沒多少,而且我 openlayer 用的本來就不多。
然后這里分享一下官網(wǎng)的測距案例
引入相關(guān)庫文件
這個庫文件直接按照官網(wǎng)的來就可以了。 首先說一個事情哈,官網(wǎng)用的案例是地圖使用的 EPSG:3857, 如果我們改成 EPSG:4326,測量數(shù)據(jù)不準(zhǔn)確,切記這一點(diǎn)。
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';
上面是我引入的庫文件,和官網(wǎng)基本上一樣。
繪制提示文字
首先我們看下圖官網(wǎng)效果,官網(wǎng)開始繪制或者是繪制中都在鼠標(biāo)旁邊有一個title文本框用來提示用戶操作信息。

我們首先來實(shí)現(xiàn)一下這個功能。
首先說明一點(diǎn)哈,這是關(guān)鍵代碼,有些參數(shù)可能用起來發(fā)現(xiàn)沒有聲明,都是全局的,自己加在全局就可以,主要是下面這一些。
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;
首先我們在需要實(shí)現(xiàn)測距功能的頁面上寫兩個按鈕,一個開始測距,一個結(jié)束測距。然后點(diǎn)擊開始測距的時候,執(zhí)行一個方法,假設(shè)是distance方法。
distance() {
let source = new VectorSource() // 首先創(chuàng)建一個數(shù)據(jù)源,用來放置繪制過程中和繪制結(jié)束后的線段
const layer = new VectorLayer({ // 添加一個圖層,用來放置數(shù)據(jù)源,樣式自己隨便設(shè)置就可以了,我這里默認(rèn)的官網(wǎng)
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) => { // 給地圖添加一個鼠標(biāo)移動事件
let helpMsg = '點(diǎn)擊開始測量' // 默認(rèn)開始的操作提示文本
if (feature) { // featuer 是全局的,判斷有沒有點(diǎn)擊鼠標(biāo)繪制過
helpMsg = '雙擊結(jié)束測量' // 如果之前點(diǎn)擊繪制了就顯示雙擊結(jié)束
}
helpTooltipElement.innerHTML = helpMsg; // 設(shè)置dom的提示文字
helpTooltip.setPosition(ev.coordinate); // 設(shè)置位置跟著鼠標(biāo)走
helpTooltipElement.classList.remove('hidden') // 讓他顯示出來
})
this.createHelpTooltip() // 創(chuàng)建那個helpTooltipElement方法
map.addLayer(layer) // 把圖層添加到地圖
},
然后調(diào)用了一個初始化操作提示的dom元素。這個就是官網(wǎng)的函數(shù),如果參數(shù)名和自己起的或者是map的指向問題需要自己根據(jù)自己的實(shí)際修改一下。
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);
},
還有一點(diǎn),為了好看,把官網(wǎng)的樣式復(fù)制一下子。
<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>
然后就可以看到我們點(diǎn)擊“開始測距”按鈕之后,上面代碼執(zhí)行,鼠標(biāo)旁邊就出現(xiàn)一個小小的操作提示。

鼠標(biāo)繪制線
好的,通過上面的代碼呢,我們成功的繪制出了提示框,然后就是鼠標(biāo)繪制,代碼也很簡單,在map監(jiān)聽的pointermove方法中,繼續(xù)創(chuàng)建一個draw進(jìn)行繪制,關(guān)鍵代碼就是下面:
draw = new Draw({
source, // 這個數(shù)據(jù)源就是我們最開始的那個數(shù)據(jù)源,這是簡寫,實(shí)際上是 source:source,
type: 'LineString', // 繪制線
style: new Style({ // 繪制完成之前線的樣式,這是官網(wǎng)的樣式,需要的話自己可以修改成自己想要的樣子
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 綁定到地圖上面去
然后就實(shí)現(xiàn)了鼠標(biāo)繪制線。

設(shè)置距離信息窗
在我們點(diǎn)擊開始測量的時候呢,在我們拖動鼠標(biāo)的時候,會在上方顯示出當(dāng)前距離起點(diǎn)的距離,這個地方代碼實(shí)現(xiàn)就是下面的樣子,繼續(xù)在上面的代碼后面寫:
// 開始監(jiān)聽繪制
draw.on('drawstart', (evt) => {
feature = evt.feature; // feature就是全局的
let tooltipCoord = evt.coordinate; // 鼠標(biāo)當(dāng)前的位置
listener = feature.getGeometry().on('change', function (evt) {
const geom = evt.target;
let output = formatLength(geom); // 距離的格式
tooltipCoord = geom.getLastCoordinate(); // 設(shè)置鼠標(biāo)位置改變后的實(shí)時位置
measureTooltipElement.innerHTML = output; // 設(shè)置提示框的內(nèi)容,就是距離
measureTooltip.setPosition(tooltipCoord); // 設(shè)置距離提示框的位置
});
});
// 格式化長度, 直接官網(wǎng)代碼
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() // 創(chuàng)建那個距離的提示框然后上面代碼調(diào)用了一個方法。
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);
},
完成上面的代碼之后,我們在點(diǎn)擊開始測量之后,會在鼠標(biāo)上方實(shí)時顯示當(dāng)前鼠標(biāo)位置距離起點(diǎn)的距離。

繪制完成
上邊已經(jīng)實(shí)現(xiàn)了點(diǎn)擊開始測距,并且實(shí)時顯示距離信息,接下來就是雙擊完成時候顯示出總長度。
繼續(xù)在之前代碼后邊寫:
// 雙擊繪制完成
draw.on('drawend', () => {
measureTooltipElement.className = 'ol-tooltip ol-tooltip-static';
measureTooltip.setOffset([0, -7]);
feature = null;
measureTooltipElement = null;
this.createMeasureTooltip();
unByKey(listener);
});
上邊的代碼基本上就是官網(wǎng)的代碼,但是變量名不一樣的地方需要稍微改一下。
通過上面的代碼就實(shí)現(xiàn)了雙擊測量完成的功能。

OK,到此為止,測距功能全部完成!
取消繪制
繪制功能完成了,就需要取消繪制,取消繪制需要在點(diǎn)擊“取消繪制”按鈕之后,取消地圖繪制功能,刪除界面上已經(jīng)繪制過的內(nèi)容。
首先我們需要刪除地圖上繪制過的內(nèi)容,包括連線,以及彈窗。
這個地方需要注意一下,我們需要把繪制的圖層,比如連線,和彈窗都保存到一個或者是幾個列表里面,然后在點(diǎn)擊按鈕的時候,去遍歷刪除。
所以說我們要在點(diǎn)擊測距時候加載到地圖的圖層之后,將創(chuàng)建的圖層添加到一個數(shù)組存起來。
map.addLayer(layer) this.drawLayers.push(layer) // 保存起來
包括那個總距離的彈窗。
this.drawElements.push(measureTooltip) map.addOverlay(measureTooltip); // 保存起來
然后點(diǎn)擊“取消測量”按鈕的時候執(zhí)行下面的代碼:
// 取消繪制
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);
},
這樣就可以了。

這樣就完成了!
全部代碼
這里分享一下全部代碼,就不放資源了,下載還花錢,我也是跟人家學(xué)的,沒必要都。
<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, // 設(shè)置縮放級別為整數(shù)
smoothResolutionConstraint: false, // 關(guān)閉無級縮放地圖
}),
});
},
// 測距
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 = '點(diǎn)擊開始測量'
if (feature) {
helpMsg = '雙擊結(jié)束測量'
}
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實(shí)現(xiàn)測距功能 的詳細(xì)內(nèi)容,更多關(guān)于Vue OpenLayer測距的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Vue中函數(shù)防抖節(jié)流的理解及應(yīng)用實(shí)現(xiàn)
這篇文章主要介紹了Vue中函數(shù)防抖節(jié)流的理解及應(yīng)用實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-04-04
Vue3 SFC 和 TSX 方式自定義組件實(shí)現(xiàn) v-model的詳細(xì)
v-model 是 vue3 中的一個內(nèi)置指令,很多表單元素都可以使用這個屬性,如 input、checkbox 等,咱可以在自定義組件中實(shí)現(xiàn) v-model,這篇文章主要介紹了Vue3 SFC 和 TSX 方式自定義組件實(shí)現(xiàn) v-model,需要的朋友可以參考下2022-10-10
vue3新擬態(tài)組件庫開發(fā)流程之table組件源碼分析
這篇文章主要介紹了vue3新擬態(tài)組件庫開發(fā)流程——table組件源碼,本文通過示例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-04-04
Vue.js響應(yīng)式數(shù)據(jù)的簡單實(shí)現(xiàn)方法(一看就會)
Vue最巧妙的特性之一是其響應(yīng)式系統(tǒng),下面這篇文章主要給大家介紹了關(guān)于Vue.js響應(yīng)式數(shù)據(jù)的簡單實(shí)現(xiàn)方法,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-03-03
Vue項目全局配置頁面緩存之按需讀取緩存的實(shí)現(xiàn)詳解
這篇文章主要給大家介紹了關(guān)于Vue項目全局配置頁面緩存之實(shí)現(xiàn)按需讀取緩存的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起看看吧2018-08-08
使用mint-ui實(shí)現(xiàn)省市區(qū)三級聯(lián)動效果的示例代碼
下面小編就為大家分享一篇使用mint-ui實(shí)現(xiàn)省市區(qū)三級聯(lián)動效果的示例代碼,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-02-02
vue.js實(shí)現(xiàn)h5機(jī)器人聊天(測試版)
這篇文章主要為大家詳細(xì)介紹了vue.js實(shí)現(xiàn)h5機(jī)器人聊天測試版,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2020-07-07

