地图上覆盖物压盖的优化

2020-02-20 17:03:56 浏览数 (3)

概述

在做webgis的时候,会经常性的碰到地图覆盖物压盖的情况。本文讲述一种基于聚类思路的解决办法,实现使用的是openlayers4 。

效果

默认展示第一个点(第一个点可根据一些业务逻辑进行处理,文中简单的做了处理,取了第一个点),鼠标经过第一个点的时候再将其他压盖的点展示出来。

实现

1. htm

代码语言:javascript复制
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta http-equiv="x-ua-compatible" content="ie=edge">
  <title>地图叠加物</title>
  <meta name="description" content="">
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  <link rel="stylesheet" href="css/main.css">
  <link rel="stylesheet" href="https://openlayers.org/en/v4.6.5/css/ol.css" type="text/css">
</head>

<body>
  <div id="app">
    <div id="map"></div>
    <div
      :style="{left: selectedX   'px', top: selectedY   'px'}"
      :class="selectedCluster.length > 0 ? 'show': 'hide'"
      class="overlays">
      <div
        v-for="(item, index) in selectedCluster"
        :key="index"
        class="circle-overlay cluster-overlay"
        :style="{background: colorMap[item.level], marginLeft: padding / 2   'px'}">
      </div>
    </div>
  </div>

  <script src="https://openlayers.org/en/v4.6.5/build/ol.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/vue"></script>
  <script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
  <script src="js/overlay.js"></script>
</body>
</html>

2. js

代码语言:javascript复制
var that, map;

var app = new Vue({
  el: '#app',
  data: {
    size: 20,
    padding: 2,
    overlays: [
      {
        id: 1,
        coords: [11760366.56, 4662347.84],
        level: 2
      },
      {
        id: 2,
        coords: [11760366.56, 4662347.84],
        level: 1
      },
      {
        id: 3,
        coords: [11760366.56, 4662347.84],
        level: 3
      },
      {
        id: 4,
        coords: [12760366.56, 4662347.84],
        level: 2
      },
      {
        id: 5,
        coords: [12760366.56, 4662347.84],
        level: 1
      },
      {
        id: 6,
        coords: [12760366.56, 4662347.84],
        level: 3
      }
    ],
    clusterData: [],
    mapOverlays: [],
    colorMap: {
      1: 'blue',
      2: 'orange',
      3: 'red'
    },
    mapZoom: -1,
    firstInit: false,
    selectedX: 0,
    selectedY: 0,
    selectedCluster: []
  },
  mounted() {
    that = this;
    that.initMap();
  },
  watch: {
    mapZoom(newVal, oldVal) {
      if (oldVal === -1) that.initOverlays();
    }
  },
  methods: {
    initMap() {
      var osm = new ol.layer.Tile({
        source: new ol.source.OSM()
      });
      map = new ol.Map({
        controls: ol.control.defaults({
          attribution: false
        }),
        target: 'map',
        layers: [osm],
        view: new ol.View({
          minZoom: 3,
          maxZoom: 18,
          center: [11760366.56, 4662347.84],
          zoom: 4
        })
      });
      map.on('moveend', e => {
        that.mapZoom = map.getView().getZoom();
      });
    },
    // 创建新的聚类
    createCluster(d) {
      that.clusterData.push({
        p: d,
        data: [d]
      });
    },
    // 判断距离
    clusterTest(p1, p2) {
      const pixel1 = map.getPixelFromCoordinate(p1.coords);
      const pixel2 = map.getPixelFromCoordinate(p2.coords);
      // 判断两个点的屏幕距离是否小于图标大小:小于,是
      const dis = Math.abs(pixel1[0] - pixel2[0]);
      return dis < that.size;
    },
    // 处理聚类数据
    clusterOverlays() {
      for (var i = 0; i < that.overlays.length; i  ) {
        const d = that.overlays[i];
        let _clustered = false;
        for (var j = 0;j < that.clusterData.length;j  ) {
          const _d = that.clusterData[j].p;
          const isNear = that.clusterTest(d, _d);
          if (isNear) {
            that.clusterData[j].data.push(d);
            _clustered = true;
            break;
          }
        }
        if (!_clustered) that.createCluster(d);
      }
    },
    initOverlays() {
      that.clusterOverlays();
      // that.showAllOverlays();
      that.showFirstOverlay();
    },
    showFirstOverlay() {
      console.log(that.clusterData);
      for (var i = 0; i < that.clusterData.length; i  ) {
        const d = that.clusterData[i].p;
        const dom = document.createElement('div');
        dom.style.background = that.colorMap[d.level];
        dom.setAttribute('class', 'circle-overlay');
        dom.setAttribute('index', i);
        const overlay = new ol.Overlay({
          element: dom,
          position: d.coords,
          positioning: 'center-center',
          offset: [0, 0]
        });
        map.addOverlay(overlay);

        // 添加dom事件
        dom.addEventListener('mouseover', evt => {
          const index = evt.target.getAttribute("index");
          const coords = that.clusterData[index].p.coords;
          const pixel = map.getPixelFromCoordinate(coords);
          that.selectedX = pixel[0]   that.size / 2   that.padding;
          that.selectedY = pixel[1] - that.size / 2;

          // 删除第一个div
          const cData = that.clusterData[index].data.concat([]);
          cData.splice(0, 1);
          that.selectedCluster = cData;
        });
        dom.addEventListener('mouseout', evt => {
          that.selectedCluster = [];
        });
      }
    },
    showAllOverlays() {
      for (var i = 0; i < that.clusterData.length; i  ) {
        const d = that.clusterData[i].data;
        const coords = that.clusterData[i].p.coords;
        for (var j = 0; j < d.length; j  ) {
          const _d = d[j];
          const _xOff = j * (that.size   that.padding);
          const dom = document.createElement('div');
          dom.style.background = that.colorMap[_d.level];
          dom.setAttribute('class', 'circle-overlay');
          const overlay = new ol.Overlay({
            element: dom,
            position: coords,
            positioning: 'center-center',
            offset: [_xOff, 0]
          });
          map.addOverlay(overlay);
        }
      }
    }
  }
});

3.css

代码语言:javascript复制
.circle-overlay {
  border-radius: 50%;
  border: 2px solid #ffffff;
  box-shadow: 1px 1px 4px #ccc;
  width: 18px;
  height: 18px;
  line-height: 18px;
  text-align: center;
  cursor: pointer;
}
.overlays {
  position: absolute;
  z-index: 99;
  white-space: nowrap;
  overflow: hidden;
  &.hide {
    display: none;
    max-width: 0;
    transition: max-width 1s, display 1s;
  }
  &.show {
    display: block;
    max-width: 400px;
    transition: max-width 1s, display 1s;
  }
  .cluster-overlay {
    float: left;
  }
}

1 人点赞