说说地图中的聚类

作者: 牛老师讲webgis | 来源:发表于2018-06-20 19:20 被阅读19次

    概述

    虽然Openlayers4会有自带的聚类效果,但是有些时候是不能满足我们的业务场景的,本文结合一些业务场景,讲讲地图中的聚类展示。

    需求

    在级别比较小的时候聚类展示数据,当级别大于一定的级别的时候讲地图可视域内的所有点不做聚类全部展示出来。

    效果

    小级别 放大地图 固定级别全部展示

    实现

    在实现的时候,自己写了一个很简单的扩展myclusterlayer,代码如下:

    var myClusterLayer = function (options) {
        var self = this;
        var defaults = {
            map: null,
            clusterField: "",
            zooms: [4, 8, 12],
            distance: 256,
            data: [],
            style: null
        };
    
        //将default和options合并
        self.options = $.extend({}, defaults, options);
    
        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)) {
                    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;
    };
    

    说明
    1、传递参数

    map: ol的map对象;
    clusterField: 如果是基于属性做聚类的话可设置此参数;
    zooms: 只用到了最后一个级别,当地图大于最大最后一个值的时候,全部展示;
    distance:屏幕上的聚类距离;
    data:聚类的数据;
    style:样式(组)或者样式函数

    2、核心方法

    _clusterTest:判断是否满足聚类的条件,满足则执行_add2CluserData,不满足则执行_clusterCreate;
    _showCluster:展示聚类结果;

    调用代码如下:

                    var mycluster = new myClusterLayer({
                        map: map,
                        clusterField: "",
                        zooms: [8, 11, 14],
                        distance: 100,
                        data: result,
                        style: styleFunc
                    });
                    map.addLayer(mycluster);
            function styleFunc(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.Circle({
                            radius: 4,
                            fill: new ol.style.Fill({
                                color: 'red'
                            })
                        }),
                        text: new ol.style.Text({
                            text:name,
                            fill: new ol.style.Fill({
                                color: '#000000'
                            }),
                            textAlign:"left",
                            offsetX: 15,
                            textBaseline:"middle"
                        })
                    })
                }else{
                    var radius = Math.max(8, Math.min(count * 0.75, 20));
                    return new ol.style.Style({
                        image: new ol.style.Circle({
                            radius: radius,
                            fill: new ol.style.Fill({
                                color: '#cccccc'
                            }),
                            stroke: new ol.style.Stroke({
                                color: '#ff0000',
                                width: 1.5
                            })
                        }),
                        text: new ol.style.Text({
                            text:count.toString(),
                            fill: new ol.style.Fill({
                                color: '#000000'
                            }),
                            textAlign:"center",
                            textBaseline:"middle"
                        })
                    })
                }
            }
    

    技术博客
    CSDN:http://blog.csdn.NET/gisshixisheng
    在线教程
    https://edu.csdn.net/course/detail/799
    https://edu.csdn.net/course/detail/7471
    联系方式

    类型 内容
    qq 1004740957
    公众号 lzugis15
    e-mail niujp08@qq.com
    webgis群 452117357
    Android群 337469080
    GIS数据可视化群 458292378

    “GIS讲堂”知识星球开通了,在星球,我将提供一对一的问答服务,你问我答,期待与你相见。


    知识星球二维码 LZUGIS

    相关文章

      网友评论

        本文标题:说说地图中的聚类

        本文链接:https://www.haomeiwen.com/subject/gzwxyftx.html