OpenLayer4实现自定义地图聚类图层
前言:一直感觉不论OL还是arcgis 这个地图聚类是真的丑,实在让人看不下去,反观leaflet插件的的聚合效果那叫一个好看,个人感觉好看多了去了,那么把这个聚合效果用到OL上面去啊,这个是一个很好玩的事,本篇文章用到了自定义的聚类的扩展图层,感谢@牛老师源代码启发,在此基础上进行进一步的封装。
先来张效果图:
这张照片整的感觉都变形很多。其实一点没变形
一、自定义扩展图层下载(github)
ol.layer.myClusterLayer = function (options) {
var self = this;
self.styleFunc = function (feat) {
var attribute = feat.get("attribute");
var count = attribute.cluster.length;
if (count < 1) {
var name = attribute.data.name;
return new ol.style.Style({
image: new ol.style.Icon(/** @type {olx.style.IconOptions} */({
anchor: [0.5, 60],
anchorOrigin: 'top-right',
anchorXUnits: 'fraction',
anchorYUnits: 'pixels',
offsetOrigin: 'top-right',
offset: [0, 1],//偏移量设置
scale: 0.7, //图标缩放比例
opacity: 0.75, //透明度
src: 'data/marker-icon.png'//图标的url
})),
text: new ol.style.Text({
text: name,
fill: new ol.style.Fill({
color: '#000000'
}),
textAlign: "left",
offsetX: 5,
textBaseline: "middle"
})
})
} else {
var _smallCorlor;
var _bigCorlor;
if (count < 100) {
if (count > 50) {
_smallCorlor = "#f0cd41";
_bigCorlor = "#f5de8b";
}
else {
_smallCorlor = "#94d769";
_bigCorlor = "#cde7b1";
}
}
else {
_smallCorlor = '#f1964d';
_bigCorlor = "#f9bda2";
}
count++;
count = count.toString();
var smallRadius = count.length * 10;
smallRadius = smallRadius < 10 ? 12 : smallRadius ;
var bigRadius = smallRadius + 5;
return [
new ol.style.Style({
image: new ol.style.Circle({
radius: bigRadius,
fill: new ol.style.Fill({
color: _bigCorlor
})
}),
}),
new ol.style.Style({
image: new ol.style.Circle({
radius: smallRadius,
fill: new ol.style.Fill({
color: _smallCorlor
})
}),
text: new ol.style.Text({
text: count,
fill: new ol.style.Fill({
color: '#620022'
}),
textAlign: "center",
textBaseline: "middle"
})
}),
]
}
}
var defaults = {
map: null,
clusterField: "",
zooms: [2, 4, 8, 12],
distance: 256,
data: [],
style: self.styleFunc,
};
//将default和options合并
self.options = {
map: options.map,
clusterField: options.clusterField,
zooms: (options.zooms.length > 0 ? options.zooms : defaults.zooms),
distance: (options.distance > 0 ? options.distance : defaults.distance),
data: options.data,
style:(options.style!=null?options.style:defaults.style)
}
self.proj = self.options.map.getView().getProjection();
self.vectorSource = new ol.source.Vector({
features: []
});
self.vectorLayer = new ol.layer.Vector({
source: self.vectorSource,
style: self.options.style
});
self.clusterData = [];
//判断该点是否聚合
self._clusterTest = function (data, dataCluster) {
var _flag = false;
var _cField = self.options.clusterField;
if (_cField != "") {
_flag = data[_cField] === dataCluster[_cField];
} else {
//将地理坐标转换成屏幕坐标,进行距离判断
var _dataCoord = self._getCoordinate(data.lon, data.lat),
_cdataCoord = self._getCoordinate(dataCluster.lon, dataCluster.lat);
var _dataScrCoord = self.options.map.getPixelFromCoordinate(_dataCoord),
_cdataScrCoord = self.options.map.getPixelFromCoordinate(_cdataCoord);
var _distance = Math.sqrt(
Math.pow((_dataScrCoord[0] - _cdataScrCoord[0]), 2) +
Math.pow((_dataScrCoord[1] - _cdataScrCoord[1]), 2)
);
_flag = _distance <= self.options.distance;
}
//如果超过最大的缩放级别,数据全部展示
var _zoom = self.options.map.getView().getZoom(),
_maxZoom = self.options.zooms[self.options.zooms.length - 1];
if (_zoom > _maxZoom) _flag = false;
return _flag;
};
//坐标转换
self._getCoordinate = function (lon, lat) {
return ol.proj.transform([parseFloat(lon), parseFloat(lat)],
"EPSG:4326",
self.proj
);
};
//添加数据到聚合图
self._add2CluserData = function (index, data) {
self.clusterData[index].cluster.push(data);
};
self._clusterCreate = function (data) {
self.clusterData.push({
data: data,
cluster: []
});
};
//展示数据
self._showCluster = function () {
self.vectorSource.clear();
var _features = [];
for (var i = 0, len = self.clusterData.length; i < len; i++) {
var _cdata = self.clusterData[i];
var _coord = self._getCoordinate(_cdata.data.lon, _cdata.data.lat);
var _feature = new ol.Feature({
geometry: new ol.geom.Point(_coord),
attribute: _cdata
});
//如果聚合点里面没有数据就显示该点数据
if (_cdata.cluster.length === 0) _feature.attr = _feature.data;
_features.push(_feature);
}
self.vectorSource.addFeatures(_features);
};
self._clusterFeatures = function () {
self.clusterData = [];
//可视域处理
var _viewExtent = self.options.map.getView().calculateExtent();
//声明一个矩形,范围就是屏幕的四至
var _viewGeom = new ol.geom.Polygon.fromExtent(_viewExtent);
for (var i = 0, ilen = self.options.data.length; i < ilen; i++) {
var _data = self.options.data[i];
var _coord = self._getCoordinate(_data.lon, _data.lat);
if (_viewGeom.intersectsCoordinate(_coord)) {
//当前点是否聚合,默认是false
var _clustered = false;
for (var j = 0, jlen = self.clusterData.length; j < jlen; j++) {
var _cdata = self.clusterData[j];
if (self._clusterTest(_data, _cdata.data)) {
self._add2CluserData(j, _data);
_clustered = true;
break;
}
}
if (!_clustered) {
self._clusterCreate(_data);
}
}
}
self.vectorSource.clear();
self._showCluster();
};
self.init = function () {
self._clusterFeatures();
self.options.map.on("moveend", function () {
self._clusterFeatures();
});
};
self.init();
return self.vectorLayer;
};
ol.inherits(ol.layer.myClusterLayer, ol.layer.Vector);
下载地址:点我下载
二、demo示例
myClusterLayer图层参数option
- map是就当前地图容器
- clusterField 该参数是,是否属性聚合,如果赋值仅需要赋属性字段名即可
- distance是聚合的距离(屏幕上距离)
- data 是数据
- style是样式(不填就有默认样式)
{
map: map,
clusterField: "",
zooms: [12],
distance: 100,
data: result,
style:null
}
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>cluster</title>
<link rel="stylesheet" href="https://openlayers.org/en/v4.5.0/css/ol.css" type="text/css">
<style type="text/css">
body, #map {
border: 0px;
margin: 0px;
padding: 0px;
width: 100%;
height: 100%;
font-size: 13px;
overflow: hidden;
}
</style>
<script src="https://openlayers.org/en/v4.5.0/build/ol.js"></script>
<script src="../../Scripts/jquery/jquery-3.1.1.min.js"></script>
<script type="text/javascript" src="js/ClusterLayer-ol.js"></script>
<script type="text/javascript">
var map;
function init() {
var projection = new ol.proj.Projection({
code: 'EPSG:4326',
units: 'degrees'
});
function getNavmapLayer() {
return new ol.layer.Tile({
source: new ol.source.XYZ({
url: 'http://webrd01.is.autonavi.com/appmaptile?x={x}&y={y}&z={z}&lang=zh_cn&size=1&scale=1&style=8'//7,8
}),
projection: projection
});
}
var navlayer = getNavmapLayer();
map = new ol.Map({
controls: ol.control.defaults({
attribution: false
}),
target: 'map',
layers: [navlayer],
view: new ol.View({
projection: projection,
center: [116.456, 40.251],
zoom: 4
})
});
$.get("data/data.json", function (result) {
var mycluster = new ol.layer.myClusterLayer({
map: map,
clusterField: "",
zooms: [12],
distance: 100,
data: result,
style:null
});
map.addLayer(mycluster);
})
}
</script>
</head>
<body onLoad="init()">
<div id="map">
</div>
</body>
</html>