Files
wvp-platform/web/src/views/common/MapComponent_bak.vue
2025-10-31 17:06:11 +08:00

808 lines
23 KiB
Vue
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div id="mapContainer" ref="mapContainer" style="width: 100%;height: 100%;" />
</template>
<script>
import 'ol/ol.css'
import Map from 'ol/Map'
import OSM from 'ol/source/OSM'
import XYZ from 'ol/source/XYZ'
import VectorSource from 'ol/source/Vector'
import Tile from 'ol/layer/Tile'
import VectorLayer from 'ol/layer/Vector'
import LayerGroup from 'ol/layer/Group'
import MVT from 'ol/format/MVT.js'
import VectorTileLayer from 'ol/layer/VectorTile.js'
import WebGLVectorTileLayer from 'ol/layer/WebGLVectorTile.js'
import VectorTileSource from 'ol/source/VectorTile.js'
import WebGLVectorLayer from 'ol/layer/WebGLVector'
import Style from 'ol/style/Style'
import Stroke from 'ol/style/Stroke'
import Icon from 'ol/style/Icon'
import View from 'ol/View'
import Feature from 'ol/Feature'
import Overlay from 'ol/Overlay'
import { Point, LineString } from 'ol/geom'
import { get as getProj } from 'ol/proj'
import { containsCoordinate } from 'ol/extent'
import { defaults as defaultInteractions } from 'ol/interaction'
import Draw, { createBox } from 'ol/interaction/Draw'
import DragInteraction from './map/DragInteraction'
import { fromLonLat, toLonLat } from './map/TransformLonLat'
import { v4 } from 'uuid'
import { getUid } from 'ol'
import {Fill} from "ol/style";
let olMap, tileLayer = null
export default {
name: 'MapComponent',
props: [],
data() {
return {
overlayId: null,
dragInteraction: new DragInteraction(),
mapTileList: [],
mapTileIndex: 0
}
},
created() {
this.$nextTick(() => {
this.init()
})
},
mounted() {
},
destroyed() {
},
methods: {
init() {
this.$store.dispatch('server/getMapConfig')
.then(mapConfigList => {
if (mapConfigList.length === 0) {
if (window.mapParam.tilesUrl) {
this.mapTileList.push({
tilesUrl: window.mapParam.tilesUrl,
coordinateSystem: window.mapParam.coordinateSystem
})
}
}else {
this.mapTileList = mapConfigList
}
this.initMap()
})
},
initMap(){
let center = fromLonLat([116.41020, 39.915119])
window.coordinateSystem = this.mapTileList[this.mapTileIndex].coordinateSystem
if (window.mapParam.center) {
center = fromLonLat(window.mapParam.center)
}
const view = new View({
center: center,
zoom: window.mapParam.zoom || 10,
projection: this.projection,
maxZoom: window.mapParam.maxZoom || 19,
minZoom: window.mapParam.minZoom || 1
})
if (this.mapTileList.length > 0 && this.mapTileList[this.mapTileIndex].tilesUrl) {
tileLayer = new Tile({
source: new XYZ({
projection: getProj('EPSG:3857'),
wrapX: false,
tileSize: 256 || window.mapParam.tileSize,
url: this.mapTileList[this.mapTileIndex].tilesUrl
})
})
console.log(4444)
console.log(this.mapTileList[this.mapTileIndex].tilesUrl)
} else {
tileLayer = new Tile({
preload: 4,
source: new OSM()
})
}
olMap = new Map({
interactions: defaultInteractions().extend([this.dragInteraction]),
target: this.$refs.mapContainer, // 容器ID
layers: [tileLayer], // 默认图层
view: view, // 视图
controls: [ // 控件
]
})
// olMap.addControl(new ZoomSlider({
// className: 'zoom-slider'
// }))
olMap.once('loadend', event => {
this.$emit('loaded')
})
olMap.on('click', event => {
let features = {}
let layers = {}
console.log(event)
// 单个元素事件传递
olMap.forEachFeatureAtPixel(event.pixel, (featureAtPixel, layerAtPixel) => {
console.log(4444)
if (layerAtPixel) {
let ol_uid = 'key' + getUid(layerAtPixel)
layers[ol_uid] = layerAtPixel
if (Object.hasOwn(features, ol_uid)) {
features[ol_uid].push(featureAtPixel)
} else {
features[ol_uid] = new Array(featureAtPixel)
}
}
})
// 遍历图层,传递事件
for (const key in layers) {
if (Object.hasOwn(layers, key)) {
var layer = layers[key]
layer.dispatchEvent({ type: 'click', event: event, features: features[key], outParam: { layersCount: Object.keys(layers).length } });
}
}
features = {}
layer = {}
})
olMap.getView().on('change:resolution', () => {
this.$emit('zoomChange', olMap.getView().getZoom())
})
},
addVectorTileLayer(tileUrl, clickEvent){
let source = new VectorTileSource({
format: new MVT(),
url: tileUrl
})
const webglStyle = {
// Example layer names — adjust to actual layer names from your tileset:
// 'poi', 'places', 'points', 'place_label' etc.
// For layers that are not points we leave undefined so they won't be drawn (or you can omit them).
'poi': {
// Only draw circles for point geometries
symbol: {
symbolType: 'circle',
// color: rgba in 0..1 range => red color with full opacity
color: [1.0, 0.0, 0.0, 1.0],
// radius in pixels
radius: 4,
// optional outline stroke for visibility (rgba)
stroke: {
color: [1, 1, 1, 0.6],
width: 0.5
}
}
},
'places': {
symbol: {
symbolType: 'circle',
color: [1.0, 0.0, 0.0, 1.0],
radius: 4
}
},
'points': {
symbol: {
symbolType: 'circle',
color: [1.0, 0.0, 0.0, 1.0],
radius: 4
}
}
// If your tileset uses different layer names, add them here with the same circle symbol.
};
let layer = new WebGLVectorTileLayer({
source: source,
declutter: false,
disableHitDetection: true,
// style: function(feature) {
// let status = feature.properties_.gbStatus
// if (status === 'ON') {
// return new Style({
// image: new Icon({
// anchor: [0.5, 1],
// crossOrigin: 'Anonymous',
// src: 'static/images/gis/camera1.png',
// opacity: 1
// })
// })
// }else if (status === 'OFF'){
// return new Style({
// image: new Icon({
// anchor: [0.5, 1],
// crossOrigin: 'Anonymous',
// src: 'static/images/gis/camera1-offline.png',
// opacity: 1
// })
// })
// }
// }
// style: webglStyle
style: {
// 必须提供 style 配置,可以是对象或函数
// 'circle-radius': 4,
// 'circle-fill-color': 'red',
// 'circle-stroke-color': 'white',
// 'circle-stroke-width': 0.5
'icon-src': 'static/images/gis/sprite.png',
'icon-width': 120,
'icon-height': 40,
'icon-size': [40, 40],
'icon-anchor': [0.5, 1],
'icon-offset-origin': 'bottom-left',
'icon-offset': [0, 0]
// 'icon-offset': [
// 'match',
// ['get', 'status'],
// 'ON',
// [0, 0],
// 'OFF',
// [40, 0],
// 'checked',
// [80, 0],
// [120, 60]
// ]
}
})
olMap.addLayer(layer)
if (clickEvent) {
layer.on('click', (event) => {
console.log(event)
if (event.features.length > 0) {
const items = []
for (let i = 0; i < event.features.length; i++) {
items.push(event.features[i].properties_)
}
clickEvent(items)
}
})
}
},
setCenter(point) {
},
getCenter() {
return toLonLat(olMap.getView().getCenter())
},
getZoom() {
return olMap.getView().getZoom()
},
zoomIn() {
let zoom = olMap.getView().getZoom()
if (zoom >= olMap.getView().getMaxZoom()) {
return
}
olMap.getView().animate({
zoom: Math.trunc(zoom) + 1,
duration: 600
})
},
zoomOut() {
let zoom = olMap.getView().getZoom()
if (zoom <= olMap.getView().getMinZoom()) {
return
}
olMap.getView().animate({
zoom: Math.trunc(zoom) - 1,
duration: 400
})
},
centerAndZoom(point, zoom, callback) {
var zoom_ = olMap.getView().getZoom()
zoom = zoom || zoom_
var duration = 600
olMap.getView().setCenter(fromLonLat(point))
olMap.getView().animate({
zoom: zoom,
duration: duration
})
},
coordinateInView: function(point) {
return containsCoordinate(olMap.getView().calculateExtent(), fromLonLat(point))
},
panTo(point, zoom, endCallback) {
const duration = 1500
var coordinate = fromLonLat(point)
if (containsCoordinate(olMap.getView().calculateExtent(), coordinate)) {
olMap.getView().setCenter(coordinate)
if (zoom !== olMap.getView().getZoom()) {
olMap.getView().setZoom(zoom)
}
if (endCallback) {
endCallback()
}
return
}
olMap.getView().cancelAnimations()
olMap.getView().animate({
center: coordinate,
duration: duration
})
olMap.getView().animate({
zoom: zoom -2,
duration: duration / 2
}, {
zoom: zoom || olMap.getView().getZoom(),
duration: duration / 2
})
setTimeout(endCallback, duration + 100)
},
fit(layer) {
const extent = layer.getSource().getExtent()
if (extent) {
olMap.getView().fit(extent, {
duration: 600,
padding: [100, 100, 100, 100]
})
}
},
openInfoBox(position, content, offset) {
if (this.overlayId !== null) {
this.closeInfoBox(this.overlayId)
this.overlayId = null
}
const id = v4()
// let infoBox = document.createElement('div')
// infoBox.setAttribute('id', id)
// infoBox.innerHTML = content
const overlay = new Overlay({
id: id,
autoPan: true,
autoPanAnimation: {
duration: 250
},
element: content,
positioning: 'bottom-center',
offset: offset,
position: fromLonLat(position)
// className:overlayStyle.className
})
olMap.addOverlay(overlay)
this.overlayId = id
return id
},
closeInfoBox(id) {
let overlay = olMap.getOverlayById(id)
if (overlay) {
olMap.removeOverlay(overlay)
}
let element = document.getElementById(id)
if (element) {
element.remove()
}
},
/**
* 添加图层, 数据坐标系由控件内完成输入和输出永远是wgs84
* [
* {
*
* position: [119.1212,45,122],
* image: {
* src:"/images/123.png",
* anchor: [0.5, 0.5]
*
* }
* }
*
* ]
* @param data
* @param clickEvent
* @param option
*/
addPointLayer(data, clickEvent, option) {
let vectorLayer = this.createPointLayer(data, clickEvent, option)
olMap.addLayer(vectorLayer)
return vectorLayer
},
createPointLayer(data, clickEvent, option){
console.log(444)
console.log(data)
const features = []
let maxZoom = (option && option.maxZoom) ? option.maxZoom : olMap.getView().getMaxZoom()
let minZoom = (option && option.minZoom) ? option.minZoom : olMap.getView().getMinZoom()
let declutter = option && option.declutter
if (data.length > 0) {
for (let i = 0; i < data.length; i++) {
const feature = new Feature(new Point(fromLonLat(data[i].position)))
feature.setId(data[i].id)
feature.customData = data[i].data
feature.setProperties({
status: data[i].status
})
features.push(feature)
}
}
const source = new VectorSource()
if (features.length > 0) {
source.addFeatures(features)
}
const vectorLayer = new WebGLVectorLayer({
source: source,
maxZoom: maxZoom,
minZoom: minZoom,
style: {
// 必须提供 style 配置,可以是对象或函数
// 'circle-radius': 10,
// 'circle-fill-color': 'red',
// 'circle-stroke-color': 'white',
// 'circle-stroke-width': 0.5
'icon-src': 'static/images/gis/sprite.png',
'icon-width': 120,
'icon-height': 40,
'icon-size': [40, 40],
'icon-anchor': [0.5, 1],
'icon-offset-origin': 'bottom-left',
'icon-offset': [
'match',
['get', 'status'],
'ON',
[0, 0],
'OFF',
[40, 0],
'checked',
[80, 0],
[120, 60]
]
}
})
if (clickEvent && typeof clickEvent === 'function') {
vectorLayer.on('click', (event) => {
console.log(event)
if (event.features.length > 0) {
const items = []
for (let i = 0; i < event.features.length; i++) {
items.push(event.features[i].customData)
}
clickEvent(items)
}
})
}
return vectorLayer
},
createPointLayer2(data, clickEvent, option){
if (data.length > 0) {
const features = []
let maxZoom = (option && option.maxZoom) ? option.maxZoom : olMap.getView().getMaxZoom()
let minZoom = (option && option.minZoom) ? option.minZoom : olMap.getView().getMinZoom()
let declutter = option && option.declutter
for (let i = 0; i < data.length; i++) {
const feature = new Feature(new Point(fromLonLat(data[i].position)))
feature.setId(data[i].id)
feature.customData = data[i].data
const style = new Style()
style.setImage(new Icon({
anchor: data[i].image.anchor,
crossOrigin: 'Anonymous',
src: data[i].image.src,
opacity: 1
}))
feature.setStyle(style)
features.push(feature)
}
const source = new VectorSource()
source.addFeatures(features)
const vectorLayer = new VectorLayer({
source: source,
renderMode: 'image',
declutter: declutter,
maxZoom: maxZoom,
minZoom: minZoom
})
if (clickEvent && typeof clickEvent === 'function') {
vectorLayer.on('click', (event) => {
if (event.features.length > 0) {
const items = []
for (let i = 0; i < event.features.length; i++) {
items.push(event.features[i].customData)
}
clickEvent(items)
}
})
}
return vectorLayer
}
},
hasFeature (layer, id) {
if (layer instanceof LayerGroup) {
// 目前LayerGroup的情况肯定含有这个
return true
}else {
if (layer.getSource().getFeatureById(id)) {
return true
}
}
return false
},
addFeature (layer, data) {
const feature = new Feature(new Point(fromLonLat(data.position)))
feature.setId(data.id)
feature.customData = data.data
feature.setProperties({
status: data.status
})
layer.getSource().addFeature(feature)
},
updatePointLayer(layer, data, postponement) {
console.log(data)
layer.getSource().clear(true)
if (!data || data.length == 0) {
return
}
const features = []
for (let i = 0; i < data.length; i++) {
const feature = new Feature(new Point(fromLonLat(data[i].position)))
feature.setId(data[i].id)
feature.customData = data[i].data
feature.setProperties({
status: data[i].status
})
features.push(feature)
}
layer.getSource().addFeatures(features)
if (postponement) {
olMap.removeLayer(layer)
setTimeout(() => {
olMap.addLayer(layer)
}, 100)
}
return layer
},
addPointLayerGroup(data, clickEvent) {
let keys = Array.from(data.keys())
let layers = []
for (let i = 0; i < keys.length; i++) {
let zoom = keys[i]
console.log(zoom)
let vectorLayer = this.createPointLayer(data.get(zoom), clickEvent, {
minZoom : zoom
})
vectorLayer.setProperties('layerId', zoom)
if (vectorLayer) {
layers.push(vectorLayer)
}
}
let groupLayer = new LayerGroup({
layers: layers
})
olMap.addLayer(groupLayer)
return groupLayer
},
updatePointLayerGroup(layer, data, postponement) {
},
removeLayer(layer) {
olMap.removeLayer(layer)
},
clearLayer(layer) {
layer.getSource().clear(true)
},
setFeatureImageById(layer, featureId, image) {
let feature = layer.getSource().getFeatureById(featureId)
if (!feature) {
console.error('更改feature的图标时未找到图标')
return
}
let style = feature.getStyle()
style.setImage(new Icon({
anchor: image.anchor,
crossOrigin: 'Anonymous',
src: image.src
}))
feature.setStyle(style)
olMap.render()
},
setFeaturePositionById(layer, featureId, data) {
if (layer instanceof LayerGroup) {
}else {
}
let featureOld = layer.getSource().getFeatureById(featureId)
if (featureOld) {
layer.getSource().removeFeature(featureOld)
}
const feature = new Feature(new Point(fromLonLat(data.position)))
feature.setId(data.id)
feature.customData = data.data
feature.setProperties({
status: data.status
})
layer.getSource().addFeature(feature)
},
addLineLayer(positions) {
if (positions.length > 0) {
const points = []
for (let i = 0; i < positions.length; i++) {
points.push(fromLonLat(positions[i]))
}
const line = new LineString(points)
const lineFeature = new Feature(line)
lineFeature.setStyle(new Style({
stroke: new Stroke({
width: 4,
color: '#0c6d6a'
})
}))
const source = new VectorSource()
source.addFeature(lineFeature)
const vectorLayer = new VectorLayer({
source: source
})
olMap.addLayer(vectorLayer)
return vectorLayer
}
},
getCurrentCoordinateSystem() {
return this.mapTileList[this.mapTileIndex].coordinateSystem
},
changeMapTile(index) {
let center = this.getCenter()
let mapTileConfig = this.mapTileList[this.mapTileIndex]
this.mapTileIndex = index
window.coordinateSystem = this.mapTileList[this.mapTileIndex].coordinateSystem
tileLayer.getSource().setUrl(this.mapTileList[index].tilesUrl)
if (mapTileConfig.coordinateSystem !== this.mapTileList[this.mapTileIndex].coordinateSystem) {
// 发送通知
this.$emit('coordinateSystemChange', this.mapTileList[this.mapTileIndex].coordinateSystem)
// 修正地图的中心点
olMap.getView().setCenter(fromLonLat(center))
}
},
getZoomExtent(){
return [olMap.getView().getMinZoom(), olMap.getView().getMaxZoom()]
},
/**
* 根据距离计算经纬度差值,方便前端抽稀计算
* @param distance 距离, 单位:像素值
* @param zoom 地图层级,默认取值当前层级
*/
computeDiff(distance, zoom) {
if (!distance) {
return []
}
let resolution
if (!zoom) {
resolution = olMap.getView().getResolution()
}else {
resolution = olMap.getView().getResolutionForZoom(zoom)
}
let diff = resolution * distance
return toLonLat([diff, diff])[0]
// let extent = olMap.getView().calculateExtent(olMap.getSize())
//
//
// let minLng = extent[0]
// let maxLng = extent[2]
// let minLat = extent[1]
// let maxLat = extent[3]
//
// let style = new Style({
// stroke: new Stroke({
// width: 1,
// color: 'rgba(65,65,65,0.8)'
// })
// })
// const source = new VectorSource()
// let lng = minLng
// while (lng <= maxLng) {
//
// const points = [[lng, minLat], [lng, maxLat]]
// const line = new LineString(points)
// const lineFeature = new Feature(line)
// lineFeature.setStyle(style)
// source.addFeature(lineFeature)
// lng += diff
// }
//
// let lat = minLat
// while (lat <= maxLat) {
//
// const points = [[minLng, lat], [maxLng, lat]]
// console.log(points)
// const line = new LineString(points)
// const lineFeature = new Feature(line)
// lineFeature.setStyle(style)
// source.addFeature(lineFeature)
// lat += diff
// }
//
// const vectorLayer = new VectorLayer({
// source: source
// })
// olMap.addLayer(vectorLayer)
},
startDrawBox(callback) {
const source = new VectorSource({ wrapX: false })
const vectorLayer = new VectorLayer({
source: source,
style: new Style({
fill: new Fill({
color: 'rgba(255, 97, 97, 0.24)'
}),
stroke: new Stroke({
color: 'rgba(255, 97, 97, 0.84)',
width: 0
})
})
})
olMap.addLayer(vectorLayer)
let draw = new Draw({
source: source,
type: 'Circle',
geometryFunction: createBox(),
style: new Style({
fill: new Fill({
color: 'rgba(255, 97, 97, 0.24)'
}),
stroke: new Stroke({
color: 'rgba(255, 97, 97, 0.84)',
width: 0
}),
freehand: true
})
})
olMap.addInteraction(draw)
// 添加事件
draw.on('drawstart', function (event) {
source.clear()
})
draw.on('drawend', function (event) {
let geometry = event.feature.getGeometry()
let extent = geometry.getExtent()
let min = toLonLat([extent[0], extent[1]])
let max = toLonLat([extent[2], extent[3]])
callback([min[0], min[1], max[0], max[1]])
draw.abortDrawing()
olMap.removeInteraction(draw)
source.clear(true)
olMap.removeLayer(vectorLayer)
})
},
getCoordSys(){
return this.mapTileList[this.mapTileIndex].coordinateSystem
}
}
}
</script>
<style>
#mapContainer .zoom-slider {
width: 14px;
height: 200px;
right: 20px;
bottom: 400px;
border-bottom: 1px #dfdfdf solid;
border-right: 1px #dfdfdf solid;
cursor: pointer;
background-color: #FFFFFF;
border-radius: 3px;
}
#mapContainer .zoom-slider button {
position: relative;
width: 10px;
height: 10px;
border-radius: 5px;
margin: 0;
background-color: #606266;
}
</style>