<template>
<div class="box">
<div ref="emap" id="map"></div>
<div id="popup" class="ol-popup">
<a href="#" id="popup-closer" class="ol-popup-closer"></a>
<div id="popup-content"></div>
</div>
<div class="close_on">
<!-- <el-button type="primary" size="default" @click="addWind"></el-button>
<el-button type="primary" size="default" @click="closeWind">关闭风场</el-button> -->
<el-button :loading="loading" type="primary" size="mini">
<span v-if="online" @click="addWind">开启风场</span>
<span v-else @click="closeWind">关闭风场</span>
</el-button>
</div>
</div>
</template>
<script>
import 'ol/ol.css'
import Map from 'ol/Map'
import Stamen from 'ol/source/Stamen'
import VectorSource from 'ol/source/Vector'
import View from 'ol/View'
import { Heatmap as HeatmapLayer, Tile as TileLayer, Vector as LayerVec } from 'ol/layer'
import GeoJSON from 'ol/format/GeoJSON'
import olsourceOSM from 'ol/source/OSM'
import { get as getProjection, transform, fromLonLat } from 'ol/proj'
import { Vector as SourceVec, Cluster } from 'ol/source'
import { Feature, Overlay } from 'ol'
import { Point } from 'ol/geom'
import { Style, Icon, Stroke, Fill, Text, Circle } from 'ol/style'
import { WindLayer } from 'ol-wind'
export default {
name: 'heatmap',
data() {
return {
map: null,
center: [113.0521, 34.6006],
heatData: {
type: 'FeatureCollection',
features: [
{ type: 'Point', coordinates: [104.4, 31.19], count: 100 },
{ type: 'Point', coordinates: [113.3, 30.6], count: 19 },
{ type: 'Point', coordinates: [123.3, 30.6], count: 419 },
{ type: 'Point', coordinates: [105.3, 30.6], count: 319 },
{ type: 'Point', coordinates: [106.3, 30.6], count: 719 },
{ type: 'Point', coordinates: [109.3, 31.6], count: 519 },
{ type: 'Point', coordinates: [109.3, 30.6], count: 319 },
{ type: 'Point', coordinates: [108.3, 32.6], count: 139 },
{ type: 'Point', coordinates: [118.3, 31.6], count: 129 },
{ type: 'Point', coordinates: [108.3, 33.6], count: 190 },
{ type: 'Point', coordinates: [108.3, 32.6], count: 189 },
{ type: 'Point', coordinates: [100.3, 30.6], count: 1 },
{ type: 'Point', coordinates: [109.3, 30.6], count: 119 },
{ type: 'Point', coordinates: [108.3, 31.6], count: 200 },
{ type: 'Point', coordinates: [118.3, 30.6], count: 300 },
],
},
view: null,
points: [
{
address: '河南省商丘市',
name: '测试',
id: '1000',
lon: ['113.28', '34.54'],
},
{
address: '河南省郑州市',
name: '同样是测试',
id: '1001',
lon: ['113.28', '35.54'],
},
],
layer: null,
windLayers: null,
online: true,
loading: false,
}
},
methods: {
initMap() {
let _this = this
let projection = getProjection('EPSG:4326')
// 热力图层
let vector = new HeatmapLayer({
source: new VectorSource({
features: new GeoJSON().readFeatures(this.heatData, {
dataProjection: 'EPSG:4326',
featureProjection: 'EPSG:3857',
}),
}),
blur: 20,
radius: 10,
})
// 底图1
let tile = new TileLayer({
source: new olsourceOSM(),
})
// 地图中心
let view = new View({
center: transform(this.center, 'EPSG:4326', 'EPSG:3857'),
zoom: 5,
minZoom: 5,
maxZoom: 13,
})
// 实例化底图
this.map = new Map({
layers: [tile, vector],
target: 'map',
view,
})
// 创建点标记样式
let createLabelStyle = function(feature) {
let imgs = ''
if (feature.values_.id == '1000') {
imgs = '/static/warning_icon/normal_green.png'
} else {
imgs = '/static/warning_icon/normal_red.png'
}
return new Style({
image: new Icon({
scale: 1,
opacity: 1,
src: imgs,
}),
})
}
// 显示点标记
for (let i = 0; i < this.points.length; i++) {
addMarker(this.points[i])
}
function addMarker(info) {
let iconFeature = new Feature({
geometry: new Point(fromLonLat(info.lon)),
name: info.name,
id: info.id,
address: info.address,
lon: info.lon,
})
// 调用 createLabelStyle, 添加样式
iconFeature.setStyle(createLabelStyle(iconFeature))
// 矢量图层数据源
let vectorSource = new VectorSource({
features: [iconFeature],
})
// 矢量标注图层
let vectorLayer = new LayerVec({
source: vectorSource,
})
_this.map.addLayer(vectorLayer)
}
/*********************显示弹出层**************************/
let container = document.getElementById('popup')
let content = document.getElementById('popup-content')
let popupCloser = document.getElementById('popup-closer')
// 创建覆盖物图层
let overlay = new Overlay({
element: container,
autoPan: true,
})
// 鼠标移入改变样式
this.map.on('pointermove', function(e) {
let pixel = _this.map.getEventPixel(e.originalEvent)
let hit = _this.map.hasFeatureAtPixel(pixel)
_this.map.getTargetElement().style.cursor = hit ? 'pointer' : ''
})
// 设置显示内容
function addInfo(info) {
return (content.innerHTML =
"<p class='info'>" +
info.address +
'</p>' +
"<p class='info'>" +
info.id +
'</p>' +
"<p class='info'>" +
info.name +
'</p>')
}
// 鼠标点击覆盖物
this.map.on('click', function(e) {
let feature = _this.map.forEachFeatureAtPixel(e.pixel, function(feature, layer) {
return feature
})
if (feature) {
if (!feature.values_.id) {
return false
}
let coodinate = e.coordinate
content.innerHTML = ''
addInfo(feature.values_)
overlay.setPosition(fromLonLat(feature.values_.lon))
_this.map.addOverlay(overlay)
}
})
// 点击关闭事件
popupCloser.addEventListener('click', function() {
overlay.setPosition(undefined)
})
},
addWind() {
// 开启风场
let _this = this
_this.loading = true
_this.$ajax
.get('https://sakitam-fdd.github.io/wind-layer/data/wind.json')
.then(res => {
let Data = res.data
this.windLayers = new WindLayer(Data, {
windOptions: {
colorScale: [
'rgb(36,104, 180)',
'rgb(60,157, 194)',
'rgb(128,205,193 )',
'rgb(151,218,168 )',
'rgb(198,231,181)',
'rgb(238,247,217)',
'rgb(255,238,159)',
'rgb(252,217,125)',
'rgb(255,182,100)',
'rgb(252,150,75)',
'rgb(250,112,52)',
'rgb(245,64,32)',
'rgb(237,45,28)',
'rgb(220,24,32)',
'rgb(180,0,35)',
],
lineWidth: 2,
frameRate: 20,
globalAlpha: 0.6,
velocityScale: 1 / 50,
paths: 3000,
generateParticleOption: false,
},
})
_this.$nextTick(() => {
_this.map.addLayer(_this.windLayers)
_this.online = false
_this.loading = false
_this.$message({
type: 'success',
message: '开启风场成功',
})
})
})
.catch(e => {
console.log(e)
})
},
closeWind() {
// 关闭风场
this.map.removeLayer(this.windLayers)
this.online = this.online ? false : true
this.$message({
type: 'success',
message: '关闭风场成功!',
})
},
},
mounted() {
this.initMap()
},
}
</script>
<style>
#popup-content .info {
margin: 0px;
padding: 0px;
}
</style>
<style scoped>
.box {
position: relative;
}
.label {
font-size: 20px;
}
#map {
width: 100%;
height: 99vh;
}
.ol-popup {
position: absolute;
background-color: #eeeeee;
-webkit-filter: drop-shadow(0 1px 4px rgba(0, 0, 0, 0.2));
filter: drop-shadow(0 1px 4px rgba(0, 0, 0, 0.2));
padding: 15px;
border-radius: 10px;
border: 1px solid #cccccc;
bottom: 12px;
left: -100px;
width: 180px;
}
.ol-popup:after,
.ol-popup:before {
top: 100%;
border: solid transparent;
content: '';
height: 0;
width: 0;
position: absolute;
pointer-events: none;
}
.ol-popup:after {
border-top-color: #eeeeee;
border-width: 10px;
left: 48px;
margin-left: 40px;
}
.ol-popup:before {
border-top-color: #cccccc;
border-width: 10px;
left: 48px;
margin-left: 40px;
}
.ol-popup-closer {
text-decoration: none;
position: absolute;
top: 2px;
right: 8px;
}
.ol-popup-closer:after {
content: '✖';
}
.close_on {
position: absolute;
top: 10px;
right: 10px;
}
</style>
参考链接:
https://github.com/sakitam-fdd/wind-layer(官方github)
https://sakitam-fdd.github.io/wind-layer/
参考文章:https://blog.csdn.net/u010065726/article/details/106338194/
注意:需要安装ol和ol-wind, npm i ol ol-wind --save