mapbox点位图层
import AddPopup from "@/components/Mapbox/addPopup";
class LayerClusters{
declare map: any
declare geojsonData: any
declare isCluster: boolean
declare layerProps: any
declare dialog: Object
declare sourceId: string
declare layerClustersId: string
declare layerClustersTextId: string
declare layerUnclusteredId: string
declare popup:any
constructor(option: {
map: any,
geojsonData: Object,
isCluster: true,
layerProps: {
iconImage: string,
iconSize: string
},
dialog: {
component: string,
}
}) {
this.map = option?.map
this.geojsonData = option?.geojsonData
this.isCluster = option.isCluster
this.layerProps = option?.layerProps
this.dialog = option?.dialog
this.sourceId = 'layer-clusters' + Date.now()
this.layerClustersId = 'layer-clusters' + Date.now()
this.layerClustersTextId = 'layer-clusters-text' + Date.now()
this.layerUnclusteredId = 'layer-clusters-unclustered' + Date.now()
this.popup = undefined
this.init()
}
private init(){
// 数据源加载和图层加载分开,尽量不共用一个id
this.map.addSource(this.sourceId, {
type: 'geojson',
data: this.geojsonData,
cluster: this.isCluster, // 是否聚合 true 是
clusterMaxZoom: 14, // 最大聚合等级
clusterRadius: 50 // 聚合半径,
})
// 聚合点圆样式
this.map.addLayer({
id: this.layerClustersId,
type: 'circle',
source: this.sourceId,
filter: ['has', 'point_count'],
paint: {
// 聚合颜色:
// 100个点为 #51bbd6
// 750个点为 #f1f075
// 750以上 #f28cb1
'circle-color': [
'step',
['get', 'point_count'],
'#51bbd6',
100,
'#f1f075',
750,
'#f28cb1'
],
// 聚合圆半径
// 100个点为 20 px
// 750个点为 30 px
// 750以上 40 px
'circle-radius': [
'step',
['get', 'point_count'],
20,
100,
30,
750,
40
]
},
});
// 聚合点文字
if(this.isCluster){
this.map.addLayer({
id: this.layerClustersTextId,
type: 'symbol',
source: this.sourceId,
filter: ['has', 'point_count'],
layout: {
'text-field': ['get', 'point_count_abbreviated'],
'text-font': ['DIN Offc Pro Medium', 'Arial Unicode MS Bold'],
'text-size': 12
}
});
}
// 不聚合点样式
if(this.layerProps?.iconImage){
// 不聚合点样式
this.map.addLayer({
id: this.layerUnclusteredId,
type: 'symbol',
source: this.sourceId,
filter: ['!', ['has', 'point_count']],
layout: {
'icon-image': this.layerProps?.iconImage,
'icon-size': this.layerProps?.iconSize || 1,
"icon-allow-overlap": true,
},
});
}else {
// 不聚合点样式
this.map.addLayer({
id: this.layerUnclusteredId,
type: 'circle',
source: this.sourceId,
filter: ['!', ['has', 'point_count']],
paint: {
'circle-color': '#11b4da',
'circle-radius': 4,
'circle-stroke-width': 1,
'circle-stroke-color': '#fff'
},
});
}
}
on(callback){
this.map.on('mouseenter', this.layerClustersId, () => {
this.map.getCanvas().style.cursor = 'pointer';
});
this.map.on('mouseleave', this.layerClustersId, () => {
this.map.getCanvas().style.cursor = '';
});
this.map.on('mouseenter', this.layerUnclusteredId, () => {
this.map.getCanvas().style.cursor = 'pointer';
});
this.map.on('mouseleave', this.layerUnclusteredId, () => {
this.map.getCanvas().style.cursor = '';
});
this.map.on('click',this.layerUnclusteredId,(e)=>{
callback(e)
const coordinates = e.features[0].geometry.coordinates
this.map.panTo(coordinates)
if(this.popup){
this.popup.destory()
}
if(this.dialog?.component){
this.popup = new AddPopup({
map: this.map,
coordinates: coordinates,
dialog: {
component: this.dialog?.component,
props: {
...e.features[0].properties
}
}
})
}
})
}
setLayoutProperty(attr, attrValue){
this.map.setLayoutProperty(this.layerUnclusteredId, attr, attrValue)
}
mouse(attrName){
let that = this
this.map.on('mouseenter', this.layerUnclusteredId, (e) => {
this.map.getCanvas().style.cursor = 'pointer';
const coordinatesM = e.features[0].geometry.coordinates
const popUps = document.getElementsByClassName('my-class1')
if (popUps[0]) popUps[0].remove()
const markerHeight = 20
const markerRadius = 10
const linearOffset = 25
const popupOffsets = {
'top': [-10, 0],
'top-left': [0, 0],
'top-right': [0, 0],
'bottom': [0, -markerHeight],
'bottom-left': [linearOffset, (markerHeight - markerRadius + linearOffset) * -1],
'bottom-right': [-linearOffset, (markerHeight - markerRadius + linearOffset) * -1],
'left': [markerRadius, (markerHeight - markerRadius) * -1],
'right': [-markerRadius, (markerHeight - markerRadius) * -1]
}
new mapboxgl.Popup({offset: popupOffsets, className: 'my-class1', closeButton: false})
.setMaxWidth('none')
.setLngLat(coordinatesM)
.setHTML(`<div>${e.features[0].properties[attrName]}</div>`)
.addTo(that.map)
// if (popUps[1]) popUps[1].remove()
});
this.map.on('mouseleave', this.layerUnclusteredId, () => {
this.map.getCanvas().style.cursor = '';
const popUps = document.getElementsByClassName('my-class1')
if (popUps[0]) popUps[0].remove()
});
}
closePopup(){
if(this.popup){
this.popup.destory()
}
}
// 必须先清除layer再清除图层
destory(){
if(this.popup){
this.popup.destory()
}
if(this.map?.getSource(this.sourceId)){
this.map?.removeLayer(this.layerClustersId)
this.map?.removeLayer(this.layerUnclusteredId)
if(this.isCluster){
this.map?.removeLayer(this.layerClustersTextId)
}
this.map?.removeSource(this.sourceId)
}
}
}
export default LayerClusters
Addpopup
import {createVNode, render} from "vue";
// import mapboxgl from 'mapbox-gl'
class AddPopup{
declare map:any
declare coordinates:any
declare dialog:any
declare popup:any
declare pupupId: string
constructor(option: {
map: any,
coordinates: any,
dialog: object
}) {
this.map = option.map
this.coordinates = option.coordinates
this.dialog = option.dialog
this.popup = null
this.pupupId = `pupupId`
this.init()
}
private init(){
const app = createVNode(this.dialog?.component, {data: this.dialog?.props})
const markerHeight = 20
const markerRadius = 10
const linearOffset = 25
const popupOffsets = {
'top': [-10, 0],
'top-left': [0, 0],
'top-right': [0, 0],
'bottom': [0, -markerHeight],
'bottom-left': [linearOffset, (markerHeight - markerRadius + linearOffset) * -1],
'bottom-right': [-linearOffset, (markerHeight - markerRadius + linearOffset) * -1],
'left': [markerRadius, (markerHeight - markerRadius) * -1],
'right': [-markerRadius, (markerHeight - markerRadius) * -1]
}
this.popup = new mapboxgl.Popup({className: 'my-class', closeButton: true, offset: popupOffsets})
.setMaxWidth('none')
.setLngLat(this.coordinates)
.setHTML(`<div id="${this.pupupId}"></div>`)
.addTo(this.map)
render(app, document.querySelector(`#${this.pupupId}`))
}
destory(){
if(this.popup){
this.popup.remove()
}
if(document.querySelector(`#${this.pupupId}`)){
document.querySelector(`#${this.pupupId}`).remove()
}
}
}
export default AddPopup