前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >mapboxGL列表和地图联动

mapboxGL列表和地图联动

作者头像
牛老师讲GIS
发布2021-09-10 11:02:21
6470
发布2021-09-10 11:02:21
举报
文章被收录于专栏:跟牛老师一起学WEBGIS

概述

列表和地图联动是webgis中一个非常常见的功能,本文讲一下在mapboxGL中结合vue如何实现此功能。

效果

实现思路

1. 获取数据

获取数据并将数据保存起来;

2. 列表展示

列表简单的用ulli来实现。

3. 地图展示

数据获取之后,通过document.createElement()的方式创建marker,将结果在地图上展示。

4. 列表地图联动

联动存在两个交互:鼠标移动和点击选中,所以需要两个变量用来记录当前鼠标经过的和点击选中的,如果鼠标经过的和点击选中发生变化的时候,去设置对应的样式即可。

实现代码

代码语言:javascript
复制
<template>
  <div class="search-panel">
    <div
      class="search-result">
    <h4 class="title">
      查询结果
    </h4>
    <ul
      class="result-list">
      <li
        v-for="(item, index) in searchResult"
        :key="index"
        :class="getClass(item)"
        @mouseover="currentMarker = item"
        @mouseout="currentMarker = {}"
        @click="selectMarker = item">
        <div class="marker">
          <span>{{ index + 1 }}</span>
        </div>
        <div class="info">
          <b>{{ item.name }}</b><br>
          {{ item.addr }}
        </div>
      </li>
    </ul>
  </div>
</template>

<script>
export default {
  name: 'Search',
  data() {
    return {
      searchResult: [], //记录查询结果
      markers: [],
      currentMarker: {},
      selectMarker: {},
      markerPopup: null
    };
  },
  methods: {
    init() {
      this.markerPopup = new mapboxgl.Popup({
        closeButton: false,
        closeOnClick: false,
        className: 'marker-popup',
        offset: [0, -15],
        anchor: 'bottom'
      });
      // 造测试数据
      this.searchByKeyword();
    },
    getRandomPos() {
      const bbox = [79.740682, 20.10212, 134.0345, 44.8279];
      const lon = Math.random() * (bbox[2] - bbox[0] + 1) + bbox[0];
      const lat = Math.random() * (bbox[3] - bbox[1] + 1) + bbox[1];
      return [lon, lat];
    },
    getClass(item) { // 设置列表样式
      let cls = item.id === this.currentMarker.id ? 'active' : '';
      cls += item.id === this.selectMarker.id ? ' select' : '';
      return cls;
    },
    setMarkersClass() { // 设置地图marker样式
      this.searchResult.forEach(item => {
        const dom = document.getElementById(item.id);
        if (item.id === this.currentMarker.id) {
          dom.classList.add('active');
        } else {
          dom.classList.remove('active');
        }
        if (item.id === this.selectMarker.id) {
          dom.classList.add('select');
        } else {
          dom.classList.remove('select');
        }
      });
    },
    searchByKeyword() {
      const that = this;
      if (this.keyword !== '') {
        that.loading = true;
        that.searchResult = [];
        this.removeMarkers();
        setTimeout(() => {
          for (let i = 0; i < 10; ++i) {
            that.searchResult.push({
              id: 'marker' + i,
              name: '大石地铁站',
              addr: '广东省广州市番禺区大石地铁站',
              pos: that.getRandomPos()
            });
          }
          // 添加markers
          that.addMarkers2Map();
          that.loading = false;
        }, 2000);
      }
    },
    addMarkers2Map() {
      const that = this;
      const map = window.map;
      that.searchResult.forEach((item, index) => {
        const ele = document.createElement('div');
        ele.setAttribute('class', 'map-marker');
        ele.setAttribute('id', item.id);
        ele.innerText = (index + 1).toString();
        const option = {
          element: ele
        };
        const marker = new mapboxgl.Marker(option)
          .setLngLat(item.pos)
          .addTo(map);
        that.markers.push(marker);

        ele.onmouseover = () => {
          that.currentMarker = item;
        };
        ele.onmouseout = () => {
          that.currentMarker = {};
        };
        ele.onclick = () => {
          that.selectMarker = item;
        };
      });
    },
    removeMarkers() {
      this.markers.forEach(marker => {
        marker.remove();
      });
    },
    showMarkerInfo() {
      const that = this;
      if (this.selectMarker.id) {
        const description = `
            <h5>
               ${this.selectMarker.name}
               <span class="close el-icon-close" id="popupClose"></span>
            </h5>
            <ul>
              <li>温度: 20℃</li>
              <li>湿度: 52%</li>
              <li>降水: 1mm</li>
              <li>风速: 4m/s</li>
              <li>风向: 无持续风向</li>
            </ul>
          `;
        this.markerPopup
          .setLngLat(this.selectMarker.pos)
          .setHTML(description)
          .addTo(window.map);
        // 添加关闭事件
        document.getElementById('popupClose').onclick = () => {
          that.selectMarker = {};
        };
        window.map.flyTo({
          center: this.selectMarker.pos
        });
      } else {
        this.markerPopup.remove();
      }
    }
  },
  watch: {
    currentMarker(val) {
      this.setMarkersClass();
    },
    selectMarker(val) {
      this.setMarkersClass();
      this.showMarkerInfo();
    }
  }
};
</script>

<style scoped lang="scss">
@import '../../assets/css/map';
.search-panel {
  position: absolute;
  top: $padding;
  left: $padding;
  z-index: 99;
  .search-result {
    width: 315px;
    background-color: $bg-color;
    border-radius: 4px;
    padding-bottom: 10px;
    .title {
      margin: 0;
      padding: 10px;
      border-bottom: 1px solid #ccc;
      i {
        float: right;
        font-size: 14px;
        &:hover,
        &.active {
          cursor: pointer;
          color: $active-color;
          text-decoration: underline;
        }
      }
    }
  }
  .result-list {
    margin: 10px 0;
    min-height: 100px;
    li {
      padding: 8px 10px;
      overflow: hidden;
      border-bottom: 1px dashed #ddd;
      &:hover,
      &.active,
      &.select {
        cursor: pointer;
        .marker span {
          background-image: url('../../assets/images/marker-blue.png');
        }
      }
      &.select {
        background-color: rgba(0, 0, 0, 0.05);
      }
      .marker {
        float: left;
        width: 35px;
        text-align: center;
        line-height: 30px;
        span {
          width: 26px;
          height: 26px;
          line-height: 20px;
          text-align: center;
          display: inline-block;
          background-image: url('../../assets/images/marker-red.png');
          background-size: 100%;
          color: white;
          border-radius: 100%;
        }
      }
      .info {
        float: left;
      }
    }
  }
}
</style>
<style lang="scss">
@import '../../assets/css/map';
.map-marker {
  width: 26px;
  height: 26px;
  line-height: 20px;
  text-align: center;
  background-image: url('../../assets/images/marker-red.png');
  background-size: 100%;
  color: white;
  border-radius: 100%;
  cursor: pointer;
  &:hover,
  &.active,
  &.select {
    background-image: url('../../assets/images/marker-blue.png');
  }
}
.marker-popup {
  color: white;
  .mapboxgl-popup-content {
    background-color: $black65;
    margin: 0;
    padding: 8px;
    white-space: nowrap;
    font-size: 12px;
    h5 {
      margin: 0 0 3px 0;
      padding: 3px 0;
      font-size: 14px;
      span {
        float: right;
        cursor: pointer;
      }
    }
    label {
      display: inline-block;
      text-align: right;
      width: 65px;
    }
  }
  .mapboxgl-popup-tip {
    border-top-color: $black65 !important;
  }

  .mapboxgl-popup-close-button {
    color: white;
  }
}
</style>
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021/05/10 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 概述
  • 效果
  • 实现思路
    • 1. 获取数据
      • 2. 列表展示
        • 3. 地图展示
          • 4. 列表地图联动
          • 实现代码
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档