预加载图片 和 Marker方式打点
一、预加载图片方式打点
这种方式先将图片加载到地图上,然后添加图层时根据 mapbox 表达式 filter
来控制显示隐藏图片,这种方式实现的标记点在地图上会有聚合效果(当前视野范围内有可数标记点,当继续放大地图时,会出现更多的标记点)。
注意:出现聚合效果是因为 icon-allow-overlap
默认为 false,将 icon-allow-overlap
设置为 true,不论放大倍数是多少,都可以展示出所有标记点,不会有聚合效果 (官方属性配置:http://www.mapbox.cn/mapbox-gl-js/style-spec/#layout-symbol-icon-allow-overlap)
map组件代码:
<template>
<div>
<div ref="basicMapbox" :style="{ width: mapWidth, height: mapHeight }"></div>
<!-- 弹窗 -->
<InfoWindow v-show="showInfoWindow" ref="infoWindow"></InfoWindow>
</div>
</template>
<script>
import mapboxgl from 'mapbox-gl'
import geoJson from '@/assets/json/geoJson.json'
import pointJsonP from './json/pointJsonP' //企业标记点数据
import mapMixins from '@/mixin/map/index'
import Vue from 'vue'
import InfoWindow from './infoWindow/infoWindow.vue'
export default {
name: 'mapbox',
mixins: [mapMixins],
props: {
mapWidth: {
type: String,
default: '100%',
},
mapHeight: {
type: String,
default: '100%',
},
// 选中企业
selectedPollution: {
type: Array,
default: () => [],
},
},
components: {
InfoWindow,
},
data() {
return {
mapInst: '',
showInfoWindow: false,
// 污染企业
pollutionImg: [
{
name: '一般源',
img_md: require('@/assets/images/map/gs_md.png'),
img_lg: require('@/assets/images/map/gs_lg.png'),
status: 1,
},
{
name: '重点源',
img_md: require('@/assets/images/map/ks_md.png'),
img_lg: require('@/assets/images/map/ks_lg.png'),
status: 2,
},
{
name: '特殊源',
img_md: require('@/assets/images/map/ss_md.png'),
img_lg: require('@/assets/images/map/ss_lg.png'),
status: 3,
},
],
pFilter: ['in', 'status_p'], //污染企业
}
},
methods: {
init() {
mapboxgl.accessToken = this.mapData.accessToken
let obj = {
container: this.$refs.basicMapbox,
}
this.mapInst = new mapboxgl.Map(Object.assign(obj, this.mapData.dataObj))
// 绘制区域边界线
this.drawCityBorder(geoJson)
// 加载标记点
this.loadPointIconPollution()
},
loadPointIconPollution() {
let that = this
//that.mapInst.on('load', function () {
//加载图片
that.pollutionImg.forEach((item) => {
that.mapInst.loadImage(item.img_md, function (error, image) {
if (error) throw error
that.mapInst.addImage(item.name, image)
})
})
that.mapInst.addSource('site-pollution', {
type: 'geojson',
data: pointJsonP,
})
that.mapInst.addLayer({
id: 'site-point-pollution',
type: 'symbol',
source: 'site-pollution',
layout: {
'icon-image': ['match', ['get', 'status_p'], 1, '一般源', 2, '重点源', 3, '特殊源', /* 其他*/ ''],
},
filter: that.pFilter,
})
var pointPopup = new mapboxgl.Popup({
closeButton: true,
closeOnClick: true,
})
that.mapInst.on('click', 'site-point-pollution', function (e) {
e.preventDefault() //阻止默认事件
let latLongData = [Number(e.lngLat.lng), Number(e.lngLat.lat)]
let detailInfo = e.features[0].properties
// 点击出现弹窗
const popDetail = Vue.extend(InfoWindow)
let vm = new popDetail({
// 传参
propsData: {
detailInfo: detailInfo,
},
})
vm.$mount() //挂载
pointPopup.setLngLat(latLongData).setDOMContent(vm.$el).addTo(that.mapInst)
})
//})
},
// 更新源及图层
resetSourceLayer() {
this.mapInst.setFilter('site-point-pollution', this.pFilter)
},
},
mounted() {
this.pFilter = this.pFilter.concat(this.selectedPollution)
this.init()
},
watch: {
selectedPollution(val) {
let newFilter = ['in', 'status_p']
this.pFilter = newFilter.concat(val)
this.resetSourceLayer()
},
},
}
</script>
<style lang="scss" scoped>
// 覆盖地图弹窗默认样式
/deep/ .mapboxgl-popup-content {
background: rgba(30, 37, 48, 0.9);
border-radius: 4px;
padding: 15px;
}
/deep/ .mapboxgl-popup-tip {
border-top-color: rgba(30, 37, 48, 0.9) !important;
}
/deep/ .mapboxgl-popup-anchor-top .mapboxgl-popup-tip {
border-bottom-color: rgba(30, 37, 48, 0.9);
}
/deep/ .mapboxgl-popup {
max-width: 500px !important;
}
/deep/ .mapboxgl-popup-close-button {
display: none;
}
</style>
页面调用:
<Mapbox class="mapClass" :selectedGeneral="checkSelected" :selectedPollution="showListPollution"></Mapbox>
data(){
return{
checkSelected: [3],
showListPollution: [1, 2],
}
}
二、Marker 方式打点
地图一加载就开始打标记点,当进行筛选时则删除所有标记点,重新打点。(无聚合效果)
参考: Mapbox 添加标记点(一)
不同。
map组件代码:
<template>
<div>
<div ref="basicMapbox" :style="{ width: mapWidth, height: mapHeight }"></div>
<!-- 弹窗 -->
<InfoWindow v-show="showInfoWindow" ref="infoWindow"></InfoWindow>
</div>
</template>
<script>
import mapboxgl from 'mapbox-gl'
import geoJson from '@/assets/json/geoJson.json'
import InfoWindow from './infoWindow/infoWindow.vue'
import pointJsonC from '@/view/topic/prevention/components/map/json/pointJsonC.json' //水质类别
import mapMixins from '@/mixin/map/index' //引入地图mixins
import Vue from 'vue'
export default {
name: 'mapbox',
mixins: [mapMixins],
props: {
mapWidth: {
type: String,
default: '100%',
},
mapHeight: {
type: String,
default: '100%',
},
// 已选择的处理厂类型集合
selectedCompletion: {
type: Array,
default: () => [],
},
// 污水处理厂列表
factoryList: {
type: Array,
default: () => [],
},
},
components: {
InfoWindow,
},
data() {
return {
mapInst: '',
factoryImgList: [
{
name: '城区污水处理厂',
img_md: require('@/assets/images/map/city_md.png'),
img_lg: require('@/assets/images/map/city_lg.png'),
status: 1,
},
{
name: '乡镇污水处理厂',
img_md: require('@/assets/images/map/town_md.png'),
img_lg: require('@/assets/images/map/town_lg.png'),
status: 2,
},
],
wFilter: ['in', 'status_c'], //水质类别 // ['in', 'status_c', 1, 2, 3, 4, 5, 6]
showInfoWindow: false,
markerObjList: [],
}
},
methods: {
init() {
mapboxgl.accessToken = this.mapData.accessToken
let obj = {
container: this.$refs.basicMapbox,
}
this.mapInst = new mapboxgl.Map(Object.assign(obj, this.mapData.dataObj))
// 绘制区域边界线
this.drawCityBorder(geoJson)
// 加载标记点
// this.loadPointIcon()
},
loadPointIcon() {
let that = this
that.mapInst.on('load', function () {
// 加载图片
that.factoryImgList.forEach((item) => {
that.mapInst.loadImage(item.img_md, function (error, image) {
if (error) throw error
that.mapInst.addImage(item.name, image) // 使用 name 作为判断依据
})
})
// 加载数据源
that.mapInst.addSource('site', {
type: 'geojson',
data: pointJsonC,
})
// 完成情况标记点
that.mapInst.addLayer({
id: 'site-point-completion',
type: 'symbol',
source: 'site',
layout: {
// 如果上方图片是以 status 作为判断依据,则筛选条件是: 'icon-image': ['match', ['get', 'status_c'], 1, '1', 2, '2', 3, '3', 4, '4', 5, '5', 6, '6', /* 其他*/ '6'],
'icon-image': ['match', ['get', 'status_c'], 1, '城区污水处理厂', 2, '乡镇污水处理厂', /* 其他*/ ''],
},
filter: that.wFilter,
})
})
},
// 添加污染厂标记点
setFactoryMarker(pointList) {
var markerList = []
for (let i = 0; i < pointList.length; i++) {
let pointDetail = pointList[i]
// 过滤掉不被勾选的数据
if (this.selectedCompletion.length === 0 || this.selectedCompletion.indexOf(pointDetail.status) === -1) {
continue
}
// 点击出现弹窗
const popDetail = Vue.extend(InfoWindow)
let vm = new popDetail({
// 传参
propsData: {
detailInfo: pointDetail || {},
},
})
vm.$mount() //挂载
let pointPopup = new mapboxgl.Popup({
offset: {
top: [100, 0],
},
closeButton: true,
closeOnClick: true,
})
pointPopup.setDOMContent(vm.$el)
// 添加标记点
var floorEle = document.createElement('div')
var imgUrl = this.getFactoryImg({ status: pointDetail.status })
floorEle.innerHTML = `<div><img src="${imgUrl}" /></div>`
const marker = new mapboxgl.Marker(floorEle, {
offset: [0, 0],
})
.setLngLat([pointDetail.longitude, pointDetail.latitude])
.setPopup(pointPopup)
.addTo(this.mapInst)
markerList.push(marker)
}
this.markerObjList = markerList
},
getFactoryImg({ status }) {
for (let i = 0; i < this.factoryImgList.length; i++) {
if (status === this.factoryImgList[i]['status']) {
return this.factoryImgList[i]['img_md']
}
}
},
},
mounted() {
this.init()
// 假数据模拟标记点
setTimeout(() => {
this.setFactoryMarker(this.factoryList)
}, 1 * 1000)
},
watch: {
selectedCompletion(val) {
// 移除所有标记点
if (this.markerObjList.length > 0) {
for (let i = 0; i < this.markerObjList.length; i++) {
this.markerObjList[i].remove()
}
}
// 重新打标记点点
if (this.factoryList.length > 0) {
this.setFactoryMarker(this.factoryList)
}
},
factoryList(val) {
if (val) {
if (val.length > 0) {
// this.setFactoryMarker(val)
}
}
},
},
}
</script>
<style lang='scss' scoped>
// 覆盖地图弹窗默认样式
/deep/ .mapboxgl-popup-content {
background: rgba(30, 37, 48, 0.92);
border-radius: 4px;
padding: 15px;
}
/deep/ .mapboxgl-popup-tip {
border-top-color: rgba(30, 37, 48, 0.9) !important;
}
/deep/ .mapboxgl-popup-anchor-top .mapboxgl-popup-tip {
border-bottom-color: rgba(30, 37, 48, 0.9);
}
/deep/ .mapboxgl-popup {
max-width: 500px !important;
}
/deep/ .mapboxgl-popup-close-button {
display: none;
}
</style>
页面调用
<Mapbox class="mapClass" :selectedGeneral="checkSelected" :selectedCompletion="showListCompletion" :factoryList="factoryList"></Mapbox>
data(){
return{
checkSelected: [1],
showListCompletion: [1, 2],
factoryList: [
{
id: '001',
name: 'A镇泗污水处理厂',
longitude: 112.571073,
latitude: 29.448341,
ability: '40000吨/天',
town: 'A镇',
status: 1,
},
{
id: '002',
name: 'B镇污水处理厂',
longitude: 112.611073,
latitude: 29.468341,
town: 'B镇',
status: 2,
ability: '30000吨/天',
},
{
id: '003',
name: 'C镇污水处理厂',
longitude: 112.611073,
latitude: 29.538341,
town: 'C镇',
status: 2,
ability: '60000吨/天',
},
],
}
},
methods: {
// 图例
handleChecked(val) {
this.checkSelected = val
this.showListCompletion = val.indexOf(1) > -1 ? [1, 2] : []
},
//完成情况
changeLengedCompletion(val) {
this.showListCompletion = val
},
},
三、地图的 mixins 文件
export default {
data() {
return {
mapData: {
accessToken: 'pk.eyJ1IjoiY2FzaGluMDUyMSIsImEiOiJja3dvYWd0NzMwMHhuMm5vdnlxdnV3a2doIn0.m9Z87L7fOAhB7tm8uvpm4Q',
dataObj: {
style: 'mapbox://styles/cashin0521/ckxir1lx62o4i15nx2p5maocv',
center: [112.631073, 29.448341], // 地图初始化时的地理中心点
zoom: 10, // 初始缩放比
bearing: 0,
pitch: 50
}
}
}
},
mounted() { },
methods: {
drawCityBorder(geoJson) {
let that = this
that.mapInst.on('load', () => {
var newJson = this.deviationJson(geoJson);
that.getPolygonJson(newJson)
})
},
getPolygonJson(geoJson) {
this.mapInst.addSource('states', {
type: 'geojson',
data: geoJson
})
this.mapInst.addLayer({
id: 'state-borders',
type: 'line',
source: 'states',
layout: {},
paint: {
'line-color': '#fff',
'line-width': 4
}
})
},
// 添加厚度
getPolygonJson_1(geoJson) {
var colorList = ['#414847', '#414847', '#414847', '#414847', '#414847']
var attrAndColor = []
var beijingPolygon = geoJson
var beijingPolyline = JSON.parse(JSON.stringify(geoJson))
for (var i = 0; i < beijingPolyline.features.length; i++) {
attrAndColor.push(beijingPolyline.features[i].properties.adcode)
attrAndColor.push(colorList[i % 5])
beijingPolyline.features[i].geometry.type = 'MultiLineString' //面转线
beijingPolyline.features[i].geometry.coordinates = this.convertPolygonToPolyline(beijingPolyline.features[i].geometry.coordinates)
}
this.addPolygon(beijingPolygon, attrAndColor)
this.addPolyline(beijingPolyline)
},
// geojson格式MultiPolygon转MultiLineString
convertPolygonToPolyline(MultiPolygon) {
var MultiLineString = []
MultiPolygon.forEach((Polygon) => {
Polygon.forEach((LinearRing) => {
var LineString = LinearRing
MultiLineString.push(LineString)
})
})
return MultiLineString
},
addPolygon(data, attrAndColor) {
this.mapInst.addSource('beijingPolygonSource', {
type: 'geojson',
data: data
})
this.mapInst.addLayer({
id: 'beijingPolygonLayer',
type: 'fill-extrusion',
source: 'beijingPolygonSource',
paint: {
'fill-extrusion-vertical-gradient': true,
'fill-extrusion-color': ['match', ['number', ['get', 'adcode']], ...attrAndColor, 'red'],
'fill-extrusion-height': 1500,
'fill-extrusion-base': 0,
'fill-extrusion-opacity': 1 //'fill-extrusion-opacity': 0.8,
}
})
},
addPolyline(data) {
// 各比例尺下地图分辨率,及一个像素代表的地图单位
// 该resolutions为EPSG:900913的分辨率,及个比例尺下一个像素代表多少米
var resolutions = [
156543.03392800014,
78271.516963999937,
39135.758482000092,
19567.879240999919,
9783.9396204999593,
4891.9698102499797,
2445.9849051249898,
1222.9924525624949,
611.49622628137968,
305.74811314055756,
152.87405657041106,
76.437028285073239,
38.21851414253662,
19.10925707126831,
9.5546285356341549,
4.7773142679493699,
2.3886571339746849,
1.1943285668550503,
0.59716428355981721,
0.29858214164761665,
0.149291070823808325,
0.0746455354119041625
]
// 8 ~ 18 级
for (var i = 8; i <= 18; i++) {
// var radius = 2 * resolutions[i + 1] // 2个像素,缓冲后4个像素
var radius = 2 * resolutions[i + 2] // 3个像素,缓冲后6个像素
var lineBuffer = this.$turf.buffer(data, radius, {
units: 'meters'
})
this.mapInst.addSource('lineBufferSource-' + i, {
type: 'geojson',
data: lineBuffer
// data: data,
})
this.mapInst.addLayer({
id: 'lineBufferLayer-' + i,
type: 'fill-extrusion',
source: 'lineBufferSource-' + i,
minzoom: i - 1,
maxzoom: i + 1,
paint: {
'fill-extrusion-vertical-gradient': true,
'fill-extrusion-color': '#fff',
'fill-extrusion-height': 1800, // 挤出高度
'fill-extrusion-base': 1500, // 底部的高度。必须小于或等于挤出高度
'fill-extrusion-opacity': 1 //'fill-extrusion-opacity': 0.8,
}
})
}
},
// 对华容县矢量数据做偏移处理(mapbox地图自带的区域线有明显偏移)
// 临时方案 经纬度的偏移值是人工一点点调式得到
deviationJson(geoJson) {
var newJson = JSON.parse(JSON.stringify(geoJson))
var xdistance = 0.0061 // 经度偏移
var ydistance = 0.003 // 纬度偏移
var coordinates0 = geoJson.features[0].geometry.coordinates[0][0]
var item0 = []
for (let i = 0; i < coordinates0.length; i++) {
item0.push([coordinates0[i][0] - xdistance, coordinates0[i][1] + ydistance])
}
var coordinates1 = geoJson.features[0].geometry.coordinates[1][0]
var item1 = []
for (let i = 0; i < coordinates1.length; i++) {
item1.push([coordinates1[i][0] - xdistance, coordinates1[i][1] + ydistance])
}
newJson.features[0].geometry.coordinates[0][0] = item0
newJson.features[0].geometry.coordinates[1][0] = item1
return newJson
}
}
}
四、Marker 方式打点,更新标记点
标记点集合: drinkSectionMarkers
// 更新标记点
updateDrinkSectionMarker(features) {
if (this.drinkSectionMarkers.length > 0) {
for (let i = 0; i < this.drinkSectionMarkers.length; i++) {
this.drinkSectionMarkers[i].remove()
}
this.drinkSectionMarkers = []
}
let sectionMarkers = []
for (var i = 0; i < features.length; i++) {
let pointDetail = features[i].properties
// 点击出现弹窗
const popDetail = Vue.extend(SectionInfoWindow)
let vm = new popDetail({
// 传参
propsData: {
detailInfo: pointDetail || {},
},
})
vm.$mount() //挂载
let pointPopup = new mapboxgl.Popup({
offset: {
top: [100, 0],
},
closeButton: true,
closeOnClick: true,
})
pointPopup.setDOMContent(vm.$el)
var floorEle = document.createElement('div')
var markerClassName = 'drinkSectionTypeMarker'
var imgUrl = this.getWaterQuailtyImg({ status: pointDetail.status_d, imageKey: 'img_md' })
var isHiddenMarker = this.selectedDrinking.length === 0 ? true : false
floorEle.innerHTML = `<div class="sectionMarker ${markerClassName}" style="display: ${isHiddenMarker ? 'none' : 'block'}" data-id="${pointDetail.id}" data-status="${
pointDetail.status_d || '-1'
}" data-target="${pointDetail.target}"><img class="marker-content" src="${imgUrl}" /></div>`
const marker = new mapboxgl.Marker(floorEle, {
offset: [0, 0],
})
.setLngLat([pointDetail.longitude, pointDetail.latitude])
.setPopup(pointPopup)
.addTo(this.mapInst)
sectionMarkers.push(marker)
}
this.drinkSectionMarkers = sectionMarkers
},
// 根据条件,匹配图片
getWaterQuailtyImg({ status, imageKey }) {
for (let i = 0; i < this.waterImg.length; i++) {
if (status === this.waterImg[i]['status']) {
return this.waterImg[i][imageKey]
}
}
// 数据匹配不上,就改用灰色的默认标记点
return this.waterImg[6][imageKey]
},
五、Marker 方式打点,筛选标记点
过滤筛选标记点
标记点集合:drinkSectionMarkers
filterDrinkSectoionMarker() {
if (this.drinkSectionMarkers.length > 0) {
for (let i = 0; i < this.drinkSectionMarkers.length; i++) {
var markerHtml = this.drinkSectionMarkers[i].getElement()
var className = 'drinkSectionTypeMarker'
var markerDom = markerHtml.querySelector(`.${className}`)
var dataset = markerDom.dataset
// dataset['status'] === - 1说明没有标记类型
if ((dataset && this.selectedDrinking.indexOf(Number(dataset['status'])) > -1) || (dataset['status'] === '-1' && this.selectedDrinking.length > 0)) {
markerDom.style.display = 'block'
} else {
markerDom.style.display = 'none'
}
}
}
},