实践:vue集成使用openlayers天地图,将静态图片作为底图
最近项目上要使用openlayers做业务需求,过了这么久终于有时间闲下来写文章了。使用的ol版本:^6.5.0。使用的版本不同调用openlayers的Api会有变化,请注意。
·
目录
前言
最近项目上要使用openlayers做业务需求,过了这么久终于有时间闲下来写文章了。
使用的ol版本:^6.5.0
使用的版本不同调用openlayers的Api会有变化,请注意。
一、安装ol
npm install ol
修改package.json的版本与我的一致,删除本地package-lock.json
重新安装依赖
npm install
二、附上源码
<template>
<div style="width: calc(100vw - 488px);overflow: auto;height: 103.4vh;" id="myMap" ref="myMap"></div>
</template>
<style>
</style>
<script lang="ts">
import 'ol/ol.css';
import Map from 'ol/Map';
import View from 'ol/View';
import ImageLayer from "ol/layer/Image";
import {ImageStatic} from "ol/source";
import {Projection} from "ol/proj";
import {getCenter} from "ol/extent";
import * as olInteraction from 'ol/interaction'
import DragRotateAndZoom from 'ol/interaction/DragRotateAndZoom'
import VectorSource from "ol/source/Vector";
import VectorLayer from "ol/layer/Vector";
import {LineString, Point} from "ol/geom";
import {createStringXY} from "ol/coordinate";
import {FullScreen, MousePosition} from "ol/control";
import {defaults as defaultControls} from "ol/control";
import Zoom from "ol/control/Zoom";
import {Icon, Style, Text} from 'ol/style';
import Fill from "ol/style/Fill";
import Feature from 'ol/Feature';
import Stroke from "ol/style/Stroke";
export default {
name: "olMap",
data() {
return {
map: null, // 地图
imgx: 1432, // 当前地图宽
imgy: 1002, // 当前地图高
// 图标管理器
iconLayer: "",
// 鼠标坐标管理器
mousePositionControl: "",
// 鼠标悬浮图层
hoverFeature: null,
vectorSource: null,
// 图标集合
featureList: [],
// 轮询显示图标设备名称定时器
showDeviceTimer: null,
// 设备集合
deviceList: [
{
x: 400,
y: 500,
zIndex: 2,
imgUrl: require('../assets/1612261527776.png'),
deviceName: "精密空调",
deviceId: 1,
alarmFlag: false
}, {
x: 430,
y: 320.,
zIndex: 2,
imgUrl: require('../assets/1612261527776.png'),
deviceName: "办公室门禁",
deviceId: 2,
alarmFlag: false
}, {
x: 600,
y: 600,
// 层级
zIndex: 2,
imgUrl: require('../assets/1612261527776.png'),
deviceName: "消防烟感1",
deviceId: 3,
alarmFlag: true
}, {
x: 620,
y: 600,
zIndex: 2,
imgUrl: require('../assets/1612261527776.png'),
deviceName: "消防烟感2",
deviceId: 4,
alarmFlag: true
}
]
};
},
mounted() {
this.initMap();
},
methods: {
initMap() {
this.imgy = this.$refs.myMap.offsetHeight
this.imgx = this.$refs.myMap.offsetWidth
let extent = [0, 0, this.imgx, this.imgy]; // 获取图片的宽高
// [minx, miny, maxx, maxy].
let projection = new Projection({
code: "xkcd-image",
units: "pixels",
extent: extent
});
this.map = new Map({
target: "myMap",
// shift控制旋转
interactions: olInteraction.defaults().extend([new DragRotateAndZoom()]),
layers: [
new ImageLayer({
source: new ImageStatic({
url: require('../assets/1650275911807.jpg'), // 静态地图
projection: projection,
imageExtent: extent
})
})
],
// 缩放等级zoom当前缩放程度,maxZoom最大缩放程度
view: new View({
projection: projection,
center: getCenter(extent),
// 当前缩放倍数
zoom: 2.5,
// 最大缩放倍数
maxZoom: 7,
// 最小缩放倍数
minZoom: 1
}),
controls: defaultControls({
rotate: true,
}).extend([
new FullScreen({
tipLabel: "全屏"
}),
new Zoom({
zoomInTipLabel: "放大",
zoomOutTipLabel: "缩小"
})
])
});
/**
* 添加图标
*/
this.deviceList.forEach(item => {
this.addDevicePoint(item);
})
const vectorSource = new VectorSource()
this.iconLayer = new VectorLayer({
source: vectorSource
})
// 将图标数组添加到图层中
this.iconLayer.getSource().addFeatures(this.featureList)
// 添加图层
this.map.addLayer(this.iconLayer)
this.pollShowDeviceText();
this.mapOver();
this.mapClick();
this.mapResolution();
this.addMousePosition();
this.addInteraction();
// 修改旋转鼠标悬浮tip
document.getElementsByClassName('ol-rotate-reset')[0].attributes.title.nodeValue = '重置旋转'
},
// 添加鼠标悬浮地图中坐标
addMousePosition() {
this.removeMousePosition()
this.mousePositionControl = new MousePosition({
coordinateFormat: function (e) { // 这里格式化成 X: ** Y: **
// 将坐标保留4位小数位,并转换为字符串
let stringifyFunc = createStringXY(4)
let str = stringifyFunc(e)
return 'X: ' + str.split(',')[0] + ' ' + ' Y: ' + str.split(',')[1]
},
projection: 'EPSG:4326', // 和地图坐标系保持一致
className: 'custom-mouse-position', // css类名
target: document.getElementById('mousePosition') // 显示位置鼠标坐标位置DOM
})
// 添加控制控件到地图上即可
this.map.addControl(this.mousePositionControl)
},
// 移除鼠标位置
removeMousePosition() {
if (this.mousePositionControl) {
// 移除
this.map.removeControl(this.mousePositionControl)
this.mousePositionControl = null
}
},
// 添加图标
addDevicePoint(item) {
// 设置图片位置
const iconFeature = new Feature({
geometry: new Point([item.x, item.y])
})
// 设置样式,这里使用图片
iconFeature.setStyle([
new Style({
image: new Icon({
// 指定锚 x 值的单位。的值'fraction'表示 x 值是图标的一部分。的值'pixels'表示以像素为单位的 x 值。
anchorXUnits: 'fraction',
// 左下角为原点
anchorOrigin: 'bottom-left',
anchorYUnits: 'fraction',
anchor: [0.5, 0],
// src: item.imgUrl
src: item.imgUrl
}),
zIndex: item.zIndex
}),
new Style({
text: new Text({
text: '',
font: "14px Microsoft YaHei",
offsetY: -15,
offsetX: 15,
padding: [5, 10, 5, 15],
textAlign: 'left',
backgroundFill: new Fill({
// eslint-disable-next-line no-constant-condition
color: item.alarmFlag ? 'rgba(249, 88, 87, 0.5)' : 'rgba(0,111,255,0.5)'//提示背景色
}),
fill: new Fill({
color: "#fff"
}),
}),
zIndex: item.zIndex
}),
])
// 给图层设置设备图标name和id属性,用作独立 区分
iconFeature.name = item.deviceName
iconFeature.id_ = item.deviceId;
// 将图片Feature添加到Source
this.featureList.push(iconFeature)
},
// 鼠标悬停
mapOver() {
const _this = this
this.map.on('pointermove', evt => {
// 手指效果
var pixel = _this.map.getEventPixel(evt.originalEvent);
var hit = _this.map.hasFeatureAtPixel(pixel);
// 高亮区域取消手指效果
const hoverFeature = _this.map.getFeaturesAtPixel(pixel);
this.$refs.myMap.style.cursor = hit && hoverFeature.length >= 1 ? 'pointer' : '';
var feature = _this.map.forEachFeatureAtPixel(evt.pixel,
function (feature) {
return feature
})
if (feature !== undefined) {
this.restoreDeviceText()
// 当移入图标时
// 鼠标移入更改值
feature.style_[1].text_.text_ = feature.name
// 鼠标移入更改图标层级为优先级
feature.style_[0].zIndex_ = 3
feature.style_[1].zIndex_ = 3
// 设置当前悬浮图层
this.hoverFeature = feature
this.hoverFeature.changed()
// 停掉轮询定时器
clearInterval(this.showDeviceTimer)
this.showDeviceTimer = null;
} else {
// 当移出图标时
if (this.hoverFeature) {
// 鼠标移入更改图标层级为优先级
this.hoverFeature.style_[0].zIndex_ = 1
this.hoverFeature.style_[1].zIndex_ = 1
// 重启轮询定时器
if (!this.showDeviceTimer) {
// 将设备tip标签值设置为空
this.restoreDeviceText()
this.pollShowDeviceText();
}
}
}
})
},
// 监听地图点击事件
mapClick() {
var _this = this;
this.map.on('singleclick', evt => {
var feature = _this.map.forEachFeatureAtPixel(evt.pixel,
function (feature) {
return feature
})
console.log("feature:", feature)
})
},
// 监听地图缩放
mapResolution() {
this.map.getView().on('change:resolution', evt => {
console.log("缩放了!", evt)
console.log("当前zoom:" + this.map.getView().getZoom())
if(this.map.getView().getZoom()==7){
this.map.getView().setResolution(1)
}
})
},
// 轮询显示图标设备名称
pollShowDeviceText() {
let FeatrueList = this.iconLayer.getSource().getFeatures()
let allSize = FeatrueList.length - 1;
let currentSize = 0;
/* start
* 这里必须初始化一遍hoverFeature,不然在定时器延时等待过程中会出现拿不到hoverFeature的情况
* */
if (this.hoverFeature) {
this.restoreDeviceText()
}
this.hoverFeature = FeatrueList[currentSize]
this.hoverFeature.style_[1].text_.text_ = this.hoverFeature.name
this.hoverFeature.changed()
currentSize++;
/* end * */
this.showDeviceTimer = setInterval(() => {
if (this.hoverFeature) {
this.restoreDeviceText()
}
if (currentSize > allSize) {
currentSize = 0;
}
this.hoverFeature = FeatrueList[currentSize]
this.hoverFeature.style_[1].text_.text_ = this.hoverFeature.name
this.hoverFeature.changed()
currentSize++;
}, 3000)
},
// 将设备tip标签值设置为空
restoreDeviceText() {
this.hoverFeature.style_[1].text_.text_ = ''
this.hoverFeature.changed()
this.hoverFeature = null;
},
// 添加线段
addInteraction() {
let featureLine = new Feature({
geometry: new LineString([
[480, 630],
[900, 600],
])
});
featureLine.setStyle(new Style({
stroke: new Stroke({
color: 'rgba(197,19,19,0.7)',
width: 5
})
}))
let source = new VectorSource()
source.addFeature(featureLine)
let layer = new VectorLayer()
layer.setSource(source)
this.map.addLayer(layer)
}
},
destroyed() {
clearInterval(this.showDeviceTimer)
this.showDeviceTimer = null;
}
}
</script>
<style scoped>
/deep/ .ol-rotate {
/* 设置地图旋转回正按钮位置,避免与全屏按钮重叠*/
right: 3em;
}
</style>
三、附上使用的静态图片
四、给地图添加底图图层,旋转和全屏。设置最大最小放大倍数
this.imgy = this.$refs.myMap.offsetHeight
this.imgx = this.$refs.myMap.offsetWidth
let extent = [0, 0, this.imgx, this.imgy]; // 获取图片的宽高
// [minx, miny, maxx, maxy].
let projection = new Projection({
code: "xkcd-image",
units: "pixels",
extent: extent
});
this.map = new Map({
target: "myMap",
// shift控制旋转
interactions: olInteraction.defaults().extend([new DragRotateAndZoom()]),
layers: [
new ImageLayer({
source: new ImageStatic({
url: require('../assets/1650275911807.jpg'), // 静态地图
projection: projection,
imageExtent: extent
})
})
],
// 缩放等级zoom当前缩放程度,maxZoom最大缩放程度
view: new View({
projection: projection,
center: getCenter(extent),
// 当前缩放倍数
zoom: 2.5,
// 最大缩放倍数
maxZoom: 7,
// 最小缩放倍数
minZoom: 1
}),
controls: defaultControls({
rotate: true,
}).extend([
new FullScreen({
tipLabel: "全屏"
}),
new Zoom({
zoomInTipLabel: "放大",
zoomOutTipLabel: "缩小"
})
])
});
五、给底图添加图标图层
这里有多个图标,实际业务上是获取设备集合。所以这里使用Feature,在data数据源里边创建一个全局的Features集合,每次加一个图标就放进这个集合里边。
addDevicePoint(item) {
// 设置图片位置
const iconFeature = new Feature({
geometry: new Point([item.x, item.y])
})
// 设置样式,这里使用图片
iconFeature.setStyle([
new Style({
image: new Icon({
// 指定锚 x 值的单位。的值'fraction'表示 x 值是图标的一部分。的值'pixels'表示以像素为单位的 x 值。
anchorXUnits: 'fraction',
// 左下角为原点
anchorOrigin: 'bottom-left',
anchorYUnits: 'fraction',
anchor: [0.5, 0],
// src: item.imgUrl
src: item.imgUrl
}),
zIndex: item.zIndex
}),
new Style({
text: new Text({
text: '',
font: "14px Microsoft YaHei",
offsetY: -15,
offsetX: 15,
padding: [5, 10, 5, 15],
textAlign: 'left',
backgroundFill: new Fill({
// eslint-disable-next-line no-constant-condition
color: item.alarmFlag ? 'rgba(249, 88, 87, 0.5)' : 'rgba(0,111,255,0.5)'//提示背景色
}),
fill: new Fill({
color: "#fff"
}),
}),
zIndex: item.zIndex
}),
])
// 给图层设置设备图标name和id属性,用作独立 区分
iconFeature.name = item.deviceName
iconFeature.id_ = item.deviceId;
// 将图片Feature添加到Source
this.featureList.push(iconFeature)
},
最后用这个getSource().addFeatures。同时给整个图标图层添加数据源。
到此整个地图已经有两层图层。
第一个是地图底图图片。
第二个是图标图层。
const vectorSource = new VectorSource()
this.iconLayer = new VectorLayer({
source: vectorSource
})
// 将图标数组添加到图层中
this.iconLayer.getSource().addFeatures(this.featureList)
// 添加图层
this.map.addLayer(this.iconLayer)
六、给地图添加线段
线段这里也作为单独图层,叫线段图层
addInteraction() {
let featureLine = new Feature({
geometry: new LineString([
[480, 630],
[900, 600],
])
});
featureLine.setStyle(new Style({
stroke: new Stroke({
color: 'rgba(197,19,19,0.7)',
width: 5
})
}))
let source = new VectorSource()
source.addFeature(featureLine)
let layer = new VectorLayer()
layer.setSource(source)
this.map.addLayer(layer)
}
七、给地图添加当前鼠标坐标,悬浮事件
mapOver() {
const _this = this
this.map.on('pointermove', evt => {
// 手指效果
var pixel = _this.map.getEventPixel(evt.originalEvent);
var hit = _this.map.hasFeatureAtPixel(pixel);
// 高亮区域取消手指效果
const hoverFeature = _this.map.getFeaturesAtPixel(pixel);
this.$refs.myMap.style.cursor = hit && hoverFeature.length >= 1 ? 'pointer' : '';
var feature = _this.map.forEachFeatureAtPixel(evt.pixel,
function (feature) {
return feature
})
if (feature !== undefined) {
this.restoreDeviceText()
// 当移入图标时
// 鼠标移入更改值
feature.style_[1].text_.text_ = feature.name
// 鼠标移入更改图标层级为优先级
feature.style_[0].zIndex_ = 3
feature.style_[1].zIndex_ = 3
// 设置当前悬浮图层
this.hoverFeature = feature
this.hoverFeature.changed()
// 停掉轮询定时器
clearInterval(this.showDeviceTimer)
this.showDeviceTimer = null;
} else {
// 当移出图标时
if (this.hoverFeature) {
// 鼠标移入更改图标层级为优先级
this.hoverFeature.style_[0].zIndex_ = 1
this.hoverFeature.style_[1].zIndex_ = 1
// 重启轮询定时器
if (!this.showDeviceTimer) {
// 将设备tip标签值设置为空
this.restoreDeviceText()
this.pollShowDeviceText();
}
}
}
})
},
八、给地图添加当前鼠标点击事件
mapClick() {
var _this = this;
this.map.on('singleclick', evt => {
var feature = _this.map.forEachFeatureAtPixel(evt.pixel,
function (feature) {
return feature
})
console.log("feature:", feature)
})
},
后记
到此完成!vue集成使用openlayers天地图的基本写法已经学会了吧?
更多推荐
已为社区贡献1条内容
所有评论(0)