import Style from 'ol/style/Style' import Photo from 'ol-ext/style/Photo' import { newPoint } from '@/utils/map.js' import VectorLayer from 'ol/layer/Vector.js' import { Cluster, Vector as VectorSource } from 'ol/source.js' import { Vector } from 'ol/layer.js' import * as KMap from '@/utils/ol-map/KMap.js' import { Fill, Text, Icon, Stroke } from 'ol/style.js' // import eventBus from '@/api/eventBus.js' import { extractCoordinates } from '@/common/commonFun.js' import treeImg from '@/assets/img/map/tree.png' import treeActiveImg from '@/assets/img/map/tree-active.png' import activeBgImg from '@/assets/img/map/active-bg.png' import hereImg from '@/assets/img/map/here.png' import progressImg from '@/assets/img/map/progress.png' import progressActiveImg from '@/assets/img/map/progress-active.png' /** * 果树聚合点位图层 */ class ClusterPointsLayer { constructor(kmap) { const that = this this.cloudFilenameCache = {} this.statusTitleStyleCache = {} this.textBgStyleCache = {} this.bgStyleCache = {} this.activeBgStyleCache = {} this.progressBgStyleCache = {} this.enterSelectTree = false this.selectableActiveTree = null that.initLayer(kmap) that.kmap = kmap // that.addMapSingerClick() this.currentPoint = null } addMapSingerClick() { const that = this that.kmap.on('singleclick', (evt) => { that.kmap.map.forEachFeatureAtPixel(evt.pixel, function (feature, layer) { if (layer instanceof VectorLayer && layer.get('name') === 'clusterPointsLayer') { const fs = that.selectFeature(feature) // if (that.selectableActiveTree) { // that.selectableActiveTree.set('selectableActive', false) // } // if (that.enterSelectTree) { // // eventBus.emit('showTreePopup', fs.get('sampleId')) // that.selectableActiveTree = fs // fs.set('selectableActive', true) // return // } // const miniUserId = fs.get('miniUserId') && !fs.get('virtual') // const isMy = fs.get('my') // if (isMy) { // localStorage.setItem('showMapText', true) // } else if (miniUserId) { // // eventBus.emit('adoptMap:friendTree', { // // sampleId: fs.get('sampleId'), // // }) // } else { // // eventBus.emit('showTreePopup', fs.get('sampleId')) // that.selectableActiveTree = fs // fs.set('selectableActive', true) // } } }) }) } initLayer(kmap) { const that = this this.vectorStyle = new KMap.VectorStyle() this.clusterSource = new VectorSource({}) this.clusterLayer = new Vector({ source: new Cluster({ distance: 40, source: this.clusterSource, }), className: 'ol-layer-cluster', name: 'clusterPointsLayer', minZoom: 14.6, maxZoom: 22, zIndex: 1001, style: function (feature) { const f = that.selectFeature(feature) const isVisible = f.get('visible') !== false const selectableActive = f.get('selectableActive') if (!isVisible) { return [] } if (that.enterSelectTree || selectableActive) { return new Style({ image: new Icon({ src: selectableActive ? treeActiveImg : treeImg, scale: 0.55, }), }) } const isOther = f.get('miniUserId') ? true : false const isHigh = f.get('my') if (!isHigh && !isOther) { return new Style({ image: new Icon({ src: treeImg, scale: 0.55, }), }) } const img = f.get('icon') const count = 30 const startColor = isHigh ? '#FFD887' : '#FFFFFFb3' const endColor = isHigh ? '#ED9E1E' : '#FFFFFFb3' const styles = [ that.pointStyle(img, isHigh), that.progressStyle(count, isHigh), that.textBgStyle(count, startColor, endColor, isHigh), ] if (isHigh) { styles.push( that.activePointBgStyle(), that.statusTitleStyle( f.get('treeName'), 0, -36, '#FFFFFF', '#B38A00', 14, isHigh, ), ) if (!localStorage.getItem('showMapText')) { styles.push(that.activeTextStyle()) } } return styles }, }) kmap.addLayer(this.clusterLayer) this.kmap = kmap } setVisible(visible) { if (this.clusterLayer) { this.clusterLayer.setVisible(visible) } } setData(data, enterSelectTreeVal) { const that = this this.enterSelectTree = enterSelectTreeVal if (enterSelectTreeVal) { this.setVisible(false) this.clusterSource.clear() return } this.setVisible(true) this.clusterSource.clear() const features = [] if (!data || !data.length) return const myPoint = data.find((item) => item.my) for (const item of data) { try { const f = newPoint(item, 'geom', 'cluster-point') features.push(f) } catch (err) { console.log('err', err) } } this.clusterSource.addFeatures(features) this.kmap.getView().fit(this.clusterSource.getExtent(), { duration: 1000, padding: [0, 10, 0, 10], }) if (myPoint) { setTimeout(() => { const pointPosition = extractCoordinates(myPoint.geom) const view = this.kmap.getView() view.animate({ duration: 1200, zoom: 20, center: pointPosition, }) }, 1100) } } textBgStyle(count, startColor, endColor, isHigh) { const key = count + startColor + endColor + isHigh let style = this.textBgStyleCache[key] let length = 5 let offsetX = 0 let highOffsetX = 0 if (count === 0) { length = 0 } else if (count < 7) { length = 10 offsetX = 14 highOffsetX = 14 } else if (count > 7 && count <= 14) { length = 20 offsetX = 6 highOffsetX = 7 } else if (count > 14 && count <= 21) { length = 28 offsetX = 4 highOffsetX = 5 } else if (count > 21 && count <= 29) { length = 34 offsetX = 2 highOffsetX = 3 } else if (count === 30) { length = 38 offsetX = 0 highOffsetX = 0 } if (!style) { style = new Style({ renderer: function (coordinates, state) { const ctx = state.context const x = coordinates[0] - (isHigh ? highOffsetX * state.pixelRatio : offsetX * state.pixelRatio) const y = coordinates[1] + (isHigh ? 24 * state.pixelRatio : 17 * state.pixelRatio) const width = isHigh ? (length + 10) * state.pixelRatio : length * state.pixelRatio const height = isHigh ? 10 * state.pixelRatio : 6 * state.pixelRatio const cornerRadius = isHigh ? 5 * state.pixelRatio : 3 * state.pixelRatio const gradient = ctx.createLinearGradient(x - width / 2, y, x + width / 2, y) gradient.addColorStop(0, startColor) gradient.addColorStop(1, endColor) ctx.beginPath() ctx.moveTo(x - width / 2 + cornerRadius, y - height / 2) ctx.lineTo(x + width / 2 - cornerRadius, y - height / 2) ctx.arc( x + width / 2 - cornerRadius, y - height / 2 + cornerRadius, cornerRadius, -Math.PI / 2, 0, ) ctx.lineTo(x + width / 2, y + height / 2 - cornerRadius) ctx.arc( x + width / 2 - cornerRadius, y + height / 2 - cornerRadius, cornerRadius, 0, Math.PI / 2, ) ctx.lineTo(x - width / 2 + cornerRadius, y + height / 2) ctx.arc( x - width / 2 + cornerRadius, y + height / 2 - cornerRadius, cornerRadius, Math.PI / 2, Math.PI, ) ctx.lineTo(x - width / 2, y - height / 2 + cornerRadius) ctx.arc( x - width / 2 + cornerRadius, y - height / 2 + cornerRadius, cornerRadius, Math.PI, -Math.PI / 2, ) ctx.closePath() ctx.fillStyle = gradient ctx.fill() }, zIndex: isHigh ? 12 : 10, }) this.textBgStyleCache[key] = style } return style } statusTitleStyle(statusName, offsetX, offsetY, color, strokeColor, fontSize, isHigh) { const key = statusName + '-' + offsetY let style = this.statusTitleStyleCache[key] if (!style) { style = new Style({ text: new Text({ text: statusName, offsetX: offsetX, offsetY: offsetY, font: isHigh ? 'bold 14px sans-serif' : '12px sans-serif', fill: new Fill({ color }), stroke: new Stroke({ color: strokeColor }), }), zIndex: 13, }) this.statusTitleStyleCache[key] = style } return style } pointStyle(img, isHigh) { const key = img + isHigh let bgStyle = this.bgStyleCache[key] if (!bgStyle) { bgStyle = new Style({ image: new Photo({ src: img, kind: 'circle', radius: isHigh ? 23 : 17, shadow: 0, crop: false, displacement: [0, 0], stroke: new Stroke({ width: isHigh ? 0 : 1, color: isHigh ? '#fff' : '#C7C7C7', }), }), zIndex: isHigh ? 11 : 0, }) this.bgStyleCache[key] = bgStyle } return bgStyle } activePointBgStyle() { return new Style({ image: new Icon({ src: activeBgImg, scale: 0.55, }), zIndex: 10, }) } activeTextStyle() { return new Style({ image: new Icon({ src: hereImg, scale: 0.5, displacement: [0, 108], }), zIndex: 15, }) } progressStyle(count, isHigh) { const key = count + isHigh let progressBgStyle = this.progressBgStyleCache[key] if (!progressBgStyle) { progressBgStyle = new Style({ image: new Icon({ src: isHigh ? progressActiveImg : progressImg, scale: 0.5, displacement: isHigh ? [0, -48] : [0, -34], }), zIndex: isHigh ? 12 : 0, }) this.progressBgStyleCache[key] = progressBgStyle } return progressBgStyle } selectFeature(feature) { const fs = feature.get('features') if (!fs || fs.length === 0) { return feature } if (fs.length === 1) { return fs[0] } for (const item of fs) { if (item.get('my')) { return item } } for (const item of fs) { if (item.get('miniUserId')) { return item } } return fs[0] } } export default ClusterPointsLayer