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

我們首先來實(shí)現(xiàn)一下這個(gè)功能。
首先說明一點(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;
首先我們?cè)谛枰獙?shí)現(xiàn)測(cè)距功能的頁面上寫兩個(gè)按鈕,一個(gè)開始測(cè)距,一個(gè)結(jié)束測(cè)距。然后點(diǎn)擊開始測(cè)距的時(shí)候,執(zhí)行一個(gè)方法,假設(shè)是distance方法。
distance() {
let source = new VectorSource() // 首先創(chuàng)建一個(gè)數(shù)據(jù)源,用來放置繪制過程中和繪制結(jié)束后的線段
const layer = new VectorLayer({ // 添加一個(gè)圖層,用來放置數(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) => { // 給地圖添加一個(gè)鼠標(biāo)移動(dòng)事件
let helpMsg = '點(diǎn)擊開始測(cè)量' // 默認(rèn)開始的操作提示文本
if (feature) { // featuer 是全局的,判斷有沒有點(diǎn)擊鼠標(biāo)繪制過
helpMsg = '雙擊結(jié)束測(cè)量' // 如果之前點(diǎn)擊繪制了就顯示雙擊結(jié)束
}
helpTooltipElement.innerHTML = helpMsg; // 設(shè)置dom的提示文字
helpTooltip.setPosition(ev.coordinate); // 設(shè)置位置跟著鼠標(biāo)走
helpTooltipElement.classList.remove('hidden') // 讓他顯示出來
})
this.createHelpTooltip() // 創(chuàng)建那個(gè)helpTooltipElement方法
map.addLayer(layer) // 把圖層添加到地圖
},
然后調(diào)用了一個(gè)初始化操作提示的dom元素。這個(gè)就是官網(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)擊“開始測(cè)距”按鈕之后,上面代碼執(zhí)行,鼠標(biāo)旁邊就出現(xiàn)一個(gè)小小的操作提示。

鼠標(biāo)繪制線
好的,通過上面的代碼呢,我們成功的繪制出了提示框,然后就是鼠標(biāo)繪制,代碼也很簡單,在map監(jiān)聽的pointermove方法中,繼續(xù)創(chuàng)建一個(gè)draw進(jìn)行繪制,關(guān)鍵代碼就是下面:
draw = new Draw({
source, // 這個(gè)數(shù)據(jù)源就是我們最開始的那個(gè)數(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)擊開始測(cè)量的時(shí)候呢,在我們拖動(dòng)鼠標(biāo)的時(shí)候,會(huì)在上方顯示出當(dāng)前距離起點(diǎn)的距離,這個(gè)地方代碼實(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í)時(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)建那個(gè)距離的提示框然后上面代碼調(diào)用了一個(gè)方法。
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);
},
完成上面的代碼之后,我們?cè)邳c(diǎn)擊開始測(cè)量之后,會(huì)在鼠標(biāo)上方實(shí)時(shí)顯示當(dāng)前鼠標(biāo)位置距離起點(diǎn)的距離。

繪制完成
上邊已經(jīng)實(shí)現(xiàn)了點(diǎn)擊開始測(cè)距,并且實(shí)時(shí)顯示距離信息,接下來就是雙擊完成時(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)了雙擊測(cè)量完成的功能。

OK,到此為止,測(cè)距功能全部完成!
取消繪制
繪制功能完成了,就需要取消繪制,取消繪制需要在點(diǎn)擊“取消繪制”按鈕之后,取消地圖繪制功能,刪除界面上已經(jīng)繪制過的內(nèi)容。
首先我們需要?jiǎng)h除地圖上繪制過的內(nèi)容,包括連線,以及彈窗。
這個(gè)地方需要注意一下,我們需要把繪制的圖層,比如連線,和彈窗都保存到一個(gè)或者是幾個(gè)列表里面,然后在點(diǎn)擊按鈕的時(shí)候,去遍歷刪除。
所以說我們要在點(diǎn)擊測(cè)距時(shí)候加載到地圖的圖層之后,將創(chuàng)建的圖層添加到一個(gè)數(shù)組存起來。
map.addLayer(layer) this.drawLayers.push(layer) // 保存起來
包括那個(gè)總距離的彈窗。
this.drawElements.push(measureTooltip) map.addOverlay(measureTooltip); // 保存起來
然后點(diǎn)擊“取消測(cè)量”按鈕的時(shí)候執(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()">測(cè)距</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è)置縮放級(jí)別為整數(shù)
smoothResolutionConstraint: false, // 關(guān)閉無級(jí)縮放地圖
}),
});
},
// 測(cè)距
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)擊開始測(cè)量'
if (feature) {
helpMsg = '雙擊結(jié)束測(cè)量'
}
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)',
}),
}),
}),
});
// 開始堅(jiān)挺繪制
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)測(cè)距功能 的詳細(xì)內(nèi)容,更多關(guān)于Vue OpenLayer測(cè)距的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
vue使用echarts實(shí)現(xiàn)三維圖表繪制
這篇文章主要為大家詳細(xì)介紹了vue如何在項(xiàng)目中使用echarts實(shí)現(xiàn)三維圖表的繪制,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以參考一下2023-08-08
Vue Promise解決回調(diào)地獄問題實(shí)現(xiàn)方法
這篇文章主要介紹了Vue Promise解決回調(diào)地獄問題,總的來說這并不是一道難題,那為什么要拿出這道題介紹?拿出這道題真正想要傳達(dá)的是解題的思路,以及不斷優(yōu)化探尋最優(yōu)解的過程。希望通過這道題能給你帶來一種解題優(yōu)化的思路2023-01-01
Vue transition實(shí)現(xiàn)點(diǎn)贊動(dòng)畫效果的示例
點(diǎn)贊動(dòng)畫是網(wǎng)頁評(píng)論中常見的功能,本文將介紹如何用vue實(shí)現(xiàn)這一效果。點(diǎn)贊時(shí)愛心縮小變大,變大時(shí)略微大一點(diǎn)再變正常,取消點(diǎn)贊時(shí)愛心無動(dòng)畫,同時(shí)數(shù)字滾動(dòng),+1 時(shí)向上滾動(dòng),-1 時(shí)向下滾動(dòng)2021-05-05
關(guān)于axios配置多個(gè)請(qǐng)求地址(打包后可通過配置文件修改)
這篇文章主要介紹了關(guān)于axios配置多個(gè)請(qǐng)求地址(打包后可通過配置文件修改),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-09-09
vue跳轉(zhuǎn)同一個(gè)組件,參數(shù)不同,頁面接收值只接收一次的解決方法
今天小編就為大家分享一篇vue跳轉(zhuǎn)同一個(gè)組件,參數(shù)不同,頁面接收值只接收一次的解決方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2019-11-11
在vue項(xiàng)目實(shí)現(xiàn)一個(gè)ctrl+f的搜索功能
剛剛接到領(lǐng)導(dǎo)通知,需要實(shí)現(xiàn)搜索功能,因?yàn)轫?xiàng)目是vue的而且是手機(jī)端,對(duì)我來說有點(diǎn)小難度。經(jīng)過小編的一番思索最終還是解決了,今天小編把實(shí)現(xiàn)過程分享到腳本之家平臺(tái),需要的朋友參考下2020-02-02

