Mapbox 添加标记点(二)

预加载图片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'
      }
    }
  }
},
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Windyluna

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值