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;
             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>
原文地址:https://www.cnblogs.com/tuboshu/p/10752311.html