概述
在做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;
}
}