mapbox 天地图 底图切换组件

地图却换组件

效果:
在这里插入图片描述

<template>
  <div id="layerswitch">
    <div id="right-panel">
      <div id="panel-head">
        <span class="head-text">底图</span>
        <div>
           <span @click="closePanel">
            <a-icon type="close" />
          </span>
        </div>
      </div>

      <div id="panel-body">
        <div
          v-for="(layer, idx) in baseLayers"
          :key="idx"
          class="item"
          :class="{ active: activeType == layer.lyrName }"
          @click="changeBaseLayer(layer)"
        >
          <span class="image-link" :id="('icon-' + layer.lyrName)"></span>
          <span class="image-text">{{ layer.label }}</span>
        </div>
      </div>
      <div id="panel-footer">
        <div class="header-head-text">
          <span>地名</span>
          <a-checkbox :indeterminate="indeterminate" style="float:right" :checked="checked" @change="onChange"></a-checkbox>
        </div>
        <div
          class="footer-head-text"
          v-for="(layer, idx) in overLayers"
          :key="idx"
        >
          <span>{{ layer.label }}</span>
          <a-checkbox :indeterminate="indeterminate" style="float:right" :checked="layer.isShow" @change="onChangeLayer(layer)"></a-checkbox>
        </div>
      </div>
    </div>
  </div>
</template>
//思路:把所有的图层先加上,通过属性控制显隐,比较方便,可以控制图层的显示层级
//利用排他思想,获取自己的图层显示这里的注记,和底图参数对应
<script>
import { getKey } from '@/api/tdtKey'//没有这种需求的省略,看你们地址的需求
export default {
  name: 'Layerswitch',
  components: {
  },
  data () {
    return {
      indeterminate: false,
      // checkedDa: false, // 大字版
      checked: CONFIG.baseLayers.isLabel,//true 为了配置文件好修改可以直接写,都是一样的CONFIG
      baseLayers: [],
      activeType: CONFIG.baseLayers.activeLyr,//vec
      baseLabelLayers: [
        {
          lyrName: 'cva',
          id: 'vec'
        },
        {
          lyrName: 'cia',
          id: 'img' /*  */
        },
        {
          lyrName: 'cta',
          id: 'ter'
        }
      ],
      overLayers: [], // 其他图层
      tdtKey: ''// mapbox key
    }
  },

  mounted () {
    this.getKey()
  },
  props: {
    epsg: String,
    map: Object
  },

  methods: {
    // 关闭弹框
    closePanel () {
      this.$emit('close')
    },

    async  getKey () {
      const res = await getKey()
      this.tdtKey = res.data.tdtkey//天地图的key,看自己的逻辑
      this.getAllLayers()
      this.addAllLayer()
      this.changeLabelLayer(this.activeType)
    },
    getAllLayers() { // 初始化数据
      const baseLayers = CONFIG.baseLayers.lyrs//lyrs: [ { lyrName: 'vec',label: '矢量' },{lyrName: 'img',label: '影像' /*  */},{lyrName: 'ter',label: '地形' }]
      baseLayers.forEach(item => {
        if (!Object.keys(item).includes('url')) {
          item.url = CONFIG.baseLayers.baseurl//看自己的地址
        }
        if (this.activeType === item.lyrName) {
          item.isShow = true
        } else {
          item.isShow = false
        }
      })
      this.baseLayers = baseLayers

      this.baseLabelLayers.forEach(item => {
        item.url = CONFIG.baseLayers.baseurl
        if (this.activeType === item.id) {
          item.isShow = true
        } else {
          item.isShow = false
        }
      })

      const overLayers = CONFIG.overLayers.lyrs
      // lyrs: [
     // {
      //  lyrName: 'cda',
      //  label: '大字版地名',
      //  url: [''],//我的是这样的格式,方便后续操作
       // isShow: false,
      //  minzoom: 10,
      //  maxzoom: 18
      }
      // { lyrName: '图层名字', label: '图层显示名字', url: [], isShow: true }
    ]
      overLayers.forEach(item => {
        if (item.url.length === 0) {
          item.url = CONFIG.overLayers.baseurl
        }
      })
      this.overLayers = overLayers
    },
    // 地址转换
    wmtsUrl(type, baseUrl, mapType) {
      const url = baseUrl.map(function (url) {
        return `${url}/${type}_${mapType}/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=${type}&STYLE=default&TILEMATRIXSET=${mapType}&FORMAT=tiles&TILECOL={x}&TILEROW={y}&TILEMATRIX={z}`
      })
      return url
    },
    createAllLayer (layer) { // 创建图层
      const that = this
      debugger
      let tilesC = this.wmtsUrl(layer.lyrName, layer.url, 'c')
      let tilesW = this.wmtsUrl(layer.lyrName, layer.url, 'w')

      var key = ''
      //CONFIG.key=7Z7S575KLkIZ9PYkL17LTlsVqMNTZyLK
      key = CONFIG.isdtkey ? that.tdtKey : CONFIG.isaddkey ? CONFIG.key : ''
      if (key !== '') {
        tilesC = tilesC.map(function (url) {
          return `${url}&tdtkey=${key}`
        })
        tilesW = tilesW.map(function (url) {
          return `${url}&tdtkey=${key}`
        })
      }
      const tiles = this.epsg === 'EPSG:4490' ? tilesC : tilesW
      var source = {
        type: 'raster',
        tiles: tiles,
        tileSize: 256,
        tiles_c: tilesC,//完全是因为我们需要切换坐标系功能,可以随取随用
        tiles_w: tilesW
      }
      var currentLayer = {
        id: 'layer_' + layer.lyrName,
        type: 'raster',
        source: source,
        layout: {
          visibility: layer.isShow ? 'visible' : 'none'
        },
        minzoom: layer.minzoom,
        maxzoom: layer.maxzoom
      }
      if (!Object.keys(layer).includes('minzoom')) {
        delete currentLayer.minzoom
      }
      if (!Object.keys(layer).includes('maxzoom')) {
        delete currentLayer.maxzoom
      }

      return currentLayer
    },
    addAllLayer() { // 添加图层
      this.baseLayers.forEach(layer => {
        this.map.addLayer(this.createAllLayer(layer))
      })
      this.baseLabelLayers.forEach(layer => {
        this.map.addLayer(this.createAllLayer(layer))
      })
      this.overLayers.forEach(layer => {
        this.map.addLayer(this.createAllLayer(layer))
      })
    },

    hideAllBaseLayers() { // 隐藏所有的底图
      this.baseLayers.forEach(item => {
        item.isShow = false
        var isVisible = item.isShow === true ? 'visible' : 'none'
        this.map.setLayoutProperty('layer_' + item.lyrName, 'visibility', isVisible)
      })
    },
    hideAllBaseLabelLayers() { // 隐藏所有的天地图注记
      this.baseLabelLayers.forEach(item => {
        item.isShow = false
        var isVisible = item.isShow === true ? 'visible' : 'none'
        this.map.setLayoutProperty('layer_' + item.lyrName, 'visibility', isVisible)
      })
    },
    // 切换天地图底图
    changeBaseLayer (layer) {
      if (this.activeType === layer.lyrName) {
        this.hideAllBaseLayers()
        this.activeType = ''
      } else {
        this.hideAllBaseLayers()
        layer.isShow = true
        this.activeType = layer.lyrName
        var isVisible = layer.isShow === true ? 'visible' : 'none'
        this.map.setLayoutProperty('layer_' + layer.lyrName, 'visibility', isVisible)
      }
      this.changeLabelLayer(layer.lyrName)
    },
    // 改变天地图注记
    changeLabelLayer (active) {
      if (this.checked) {
        this.hideAllBaseLabelLayers()
        this.baseLabelLayers.forEach(lableLayer => {
          if (active === lableLayer.id) {
            lableLayer.isShow = true
            var isVisible = lableLayer.isShow === true ? 'visible' : 'none'
            this.map.setLayoutProperty('layer_' + lableLayer.lyrName, 'visibility', isVisible)
          }
        })
      } else {
        this.hideAllBaseLabelLayers()
      }
    },
    // 地名切换
    onChange (e) {
      this.checked = !this.checked
      if (this.checked === false) {
        this.hideAllBaseLabelLayers()
      } else {
        this.changeLabelLayer(this.activeType)
      }
    },
    // 控制其他图层
    onChangeLayer(layer) {
      layer.isShow = !layer.isShow
      var isVisible = layer.isShow === true ? 'visible' : 'none'
      this.map.setLayoutProperty('layer_' + layer.lyrName, 'visibility', isVisible)
    }


  }
}
</script>

<style scoped>
#layerswitch {
  overflow: hidden;
  cursor: pointer;
  height: 100%;
}

#right-panel {
  margin: 5px 5px;
  min-height: 135px;
  height: 100%;
  width: 256px;
  background-color: #fff;
  padding: 5px 5px;
  box-shadow: 0 1px 1px #ddd;
}

#panel-head {
  height: 25px;
  line-height: 25px;
  padding: 0 5px;
  display: flex;
  justify-content: space-between;
}

#panel-head > span {
  line-height: 25px;
}

.head-text {
  font-size: 16px;
  font-weight: 600;
}
.header-head-text {
  display: block;
  display: flex;
  justify-content: space-between;
  font-size: 14px;
  /* font-weight: 600; */
  line-height: 25px;
  padding: 0 5px;
  border-bottom: 2px solid rgb(59, 167, 235);
}
.footer-head-text {
  display: block;
  display: flex;
  justify-content: space-between;
  font-size: 14px;
  font-weight: 600;
  line-height: 25px;
  padding: 0 5px;
  border-bottom: 1px solid #ddd;
}
#panel-body {
  /* height: 86px; */
  border-top: 1px solid #ddd;
  border-bottom: 1px solid #ddd;
  display: flex;
  flex-wrap: wrap;
}
#panel-footer {
  border-bottom: 1px solid #ddd;
  margin-bottom: 10px;
  height: calc(100% - 100px);
}
#close-btn {
  cursor: pointer;
  color: #ccc;
}

ul {
  overflow: hidden;
  list-style: none;
  padding: 0 5px;
  margin: 0;
}

li {
  /* height: 30px; */
  /* line-height: 30px; */
  border-bottom: 1px solid #ddd;
  margin-bottom: -1px;
  display: flex;
  justify-content: space-between;
}

li > span {
  font-size: 12px;
}

.item {
  position: relative;
  margin: 5px;
  width: 68px;
  text-align: center;
}
.image-link {
  display: inline-block;
  width: 68px;
  height: 63px;
}

#icon-vec {
  background: url('../assets/icon.5b1744f.png') no-repeat;//精灵图
  background-position-x: -240px;
  background-position-y: -53px;
}

#icon-img {
  background: url('../assets/icon.5b1744f.png') no-repeat;
  background-position-x: -163px;
  background-position-y: -53px;
}

#icon-ter {
  background: url('../assets/icon.5b1744f.png') no-repeat;
  background-position-x: -84px;
  background-position-y: -53px;
}

.image-text {
  position: absolute;
  bottom: 5px;
  right: 0;
  display: inline-block;
  height: 20px;
  width: 100%;
  color: #00ccff;
  text-align: center;
  font-size: 14px;
  line-height: 20px;
  background-color: rgba(0, 0, 0, 0.4);
}

.active .image-link {
  border: 2px solid #2b85e4;
}

.active .image-text {
  color: #00ccff;
}
.footer-button {
  margin-top: 5px;
  text-align: right;
}
.layer-item {
  width: 100%;
  overflow: hidden;
}
.layer-item .wrapper {
  width: 15px;
  text-align: center;
  display: inline-block;
}
.layer-item .check {
  line-height: 30px;
  float: right;
}

.item-span {
  line-height: 30px;
  font-size: 12px;
  font-weight: 600;
}
.layer-items {
  width: 100%;
  display: flex;
  justify-content: space-between;
}
.head-btn {
  font-size: 16px;
  margin-right: 10px;
  cursor: pointer;
}

.circle {
  width: 8px;
  height: 8px;
  display: inline-block;
  border-radius: 50%;
  vertical-align: middle;
}
.line {
  width: 15px;
  height: 3px;
  display: inline-block;
  transform: rotate(30deg);
}
.fill {
  width: 15px;
  height: 10px;
  display: inline-block;
}
/*滚动条样式*/
.childBox {
  max-height: 150px;
  /* height: 100%; */
  overflow: auto;
}
.childBox::-webkit-scrollbar {
  width: 0;
  background-color: #eee;
}

.childBox::-webkit-scrollbar-track {
  background-color: #eee;
}

.childBox::-webkit-scrollbar-thumb {
  background: #3db6a4;
}
</style>

父组件使用

  <!-- 切换底图 -->
    <div id="layer-switch-wrapper">
      <span
        class="switch-btn"
        v-if="(mapbuild&&styledata)"
        @click="toggleSwitchPanle"
        :title="'底图'"
      >
        <a-icon type="global" style="display:block;line-height:32px"></a-icon>
      </span>
      <div
        id="layer-switch-container"
        v-show="switchPanelShow"
      >
        <layerSwitch
          :map="map"
          @close="toggleSwitchPanle"
          :epsg="epsg"
        ></layerSwitch>
      </div>
    </div>
data(){
 	mapbuild: false,//地图加载之后置为true
      styledata: false,//样式加载之后置为true
      map: null,//地图对象
      switchPanelShow: false,
      epsg::EPSG:3857//坐标系,墨卡托的
},
 toggleSwitchPanle () {
      this.switchPanelShow = !this.switchPanelShow
    },

 initMap () {//记得初始化加载使用,记得引入mapbox 要不然没有Map对象和css否则没有样式我是在项目的index中引入过了,所以直接使用的
      if (!compassengine.supported()) {
        this.$Message.warning({
          content: '您的浏览器不支持WebGL,请升级到最新版本。',
          duration: 0
        })
        return
      }
      var style = this.style//自己的样式
      var map = new compassengine.Map({
        container: 'map',
        style: style
      })
      this.map = window.map = map

      map.on('load', () => {
        this.mapbuild = true// 地图加载完成标识
      })
      map.on('styledata', () => {
        this.styledata = true// 地图样式加载完成标识
      })
    },