修改图层展示方式

This commit is contained in:
lin
2025-10-31 17:06:11 +08:00
parent dab5643541
commit 2a5435c810
7 changed files with 907 additions and 37 deletions

View File

@@ -148,50 +148,82 @@ export default {
this.$emit('zoomChange', olMap.getView().getZoom())
})
},
addVectorTileLayer(tileUrl){
addVectorTileLayer(tileUrl, clickEvent, errorEvent){
let source = new VectorTileSource({
format: new MVT(),
url: tileUrl
})
let layer = new VectorTileLayer({
source: source,
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': [
// 'match',
// ['get', 'status'],
// 'ON',
// [0, 0],
// 'OFF',
// [40, 0],
// 'checked',
// [80, 0],
// [120, 60]
// ]
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: {
// // 必须提供 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': [
// // 'match',
// // ['get', 'status'],
// // 'ON',
// // [0, 0],
// // 'OFF',
// // [40, 0],
// // 'checked',
// // [80, 0],
// // [120, 60]
// // ]
// }
})
olMap.addLayer(layer)
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].customData)
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)
}
console.log(items)
}
})
})
}
if (errorEvent) {
source.on('tileloaderror', (event) => {
console.log(event)
errorEvent(event)
})
}
return layer
},
setCenter(point) {
@@ -708,6 +740,9 @@ export default {
source.clear(true)
olMap.removeLayer(vectorLayer)
})
},
getCoordSys(){
return this.mapTileList[this.mapTileIndex].coordinateSystem
}
}
}

View File

@@ -0,0 +1,807 @@
<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>