【问题标题】:OpenLayers Cluster RecalculateOpenLayers 集群重新计算
【发布时间】:2011-06-23 16:26:52
【问题描述】:

所以,我在 OpenLayers 中聚集了许多项目。

我正在使用一种基于项目属性对项目进行聚类的策略。

我改变了一个项目的属性。

如何获取聚类策略来重新计算集群?

【问题讨论】:

    标签: javascript openlayers


    【解决方案1】:

    好的,所以从 2.11RC1 开始,这个功能在 OpenLayers 中不可用。因此,我自己将其实现为 Cluster 类的影子。该代码也可以作为补丁在OpenLayers Trac 中获得。

    此答案末尾的代码可以直接放入 javascript 文件中,并将覆盖现有的 OpenLayers OpenLayers.Strategy.Cluster 类。它添加了一个方法recluster,当调用该方法时,将导致策略重新计算其聚类。由于我们正在更改Cluster 基类,因此任何派生类都应适当地继承recluster 方法。

    如何使用它的一个例子是:

    var clustering=new OpenLayers.Strategy.Cluster()
    var vectorlayer = new OpenLayers.Layer.Vector('Vectorlayer', {
                                                  strategies: [clustering]
    });
    
    //ADD_LOTS_OF_FEATURES_TO_VECTOR_LAYER
    
    clustering.distance=value;
    clustering.recluster();
    

    替换类的代码是:

    OpenLayers.Strategy.Cluster = OpenLayers.Class(OpenLayers.Strategy, {
    
        /**
         * APIProperty: distance
         * {Integer} Pixel distance between features that should be considered a
         *     single cluster.  Default is 20 pixels.
         */
        distance: 20,
    
        /**
         * APIProperty: threshold
         * {Integer} Optional threshold below which original features will be
         *     added to the layer instead of clusters.  For example, a threshold
         *     of 3 would mean that any time there are 2 or fewer features in
         *     a cluster, those features will be added directly to the layer instead
         *     of a cluster representing those features.  Default is null (which is
         *     equivalent to 1 - meaning that clusters may contain just one feature).
         */
        threshold: null,
    
        /**
         * Property: features
         * {Array(<OpenLayers.Feature.Vector>)} Cached features.
         */
        features: null,
    
        /**
         * Property: clusters
         * {Array(<OpenLayers.Feature.Vector>)} Calculated clusters.
         */
        clusters: null,
    
        /**
         * Property: clustering
         * {Boolean} The strategy is currently clustering features.
         */
        clustering: false,
    
        /**
         * Property: resolution
         * {Float} The resolution (map units per pixel) of the current cluster set.
         */
        resolution: null,
    
        /**
         * Constructor: OpenLayers.Strategy.Cluster
         * Create a new clustering strategy.
         *
         * Parameters:
         * options - {Object} Optional object whose properties will be set on the
         *     instance.
         */
    
        /**
         * APIMethod: activate
         * Activate the strategy.  Register any listeners, do appropriate setup.
         * 
         * Returns:
         * {Boolean} The strategy was successfully activated.
         */
        activate: function() {
            var activated = OpenLayers.Strategy.prototype.activate.call(this);
            if(activated) {
                this.layer.events.on({
                    "beforefeaturesadded": this.cacheFeatures,
                    "moveend": this.cluster,
                    scope: this
                });
            }
            return activated;
        },
    
        /**
         * APIMethod: deactivate
         * Deactivate the strategy.  Unregister any listeners, do appropriate
         *     tear-down.
         * 
         * Returns:
         * {Boolean} The strategy was successfully deactivated.
         */
        deactivate: function() {
            var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this);
            if(deactivated) {
                this.clearCache();
                this.layer.events.un({
                    "beforefeaturesadded": this.cacheFeatures,
                    "moveend": this.cluster,
                    scope: this
                });
            }
            return deactivated;
        },
    
        /**
         * Method: cacheFeatures
         * Cache features before they are added to the layer.
         *
         * Parameters:
         * event - {Object} The event that this was listening for.  This will come
         *     with a batch of features to be clustered.
         *     
         * Returns:
         * {Boolean} False to stop features from being added to the layer.
         */
        cacheFeatures: function(event) {
            var propagate = true;
            if(!this.clustering) {
                this.clearCache();
                this.features = event.features;
                this.cluster();
                propagate = false;
            }
            return propagate;
        },
    
        /**
         * Method: clearCache
         * Clear out the cached features.
         */
        clearCache: function() {
            this.features = null;
        },
    
        /**
         * Method: cluster
         * Cluster features based on some threshold distance.
         *
         * Parameters:
         * event - {Object} The event received when cluster is called as a
         *     result of a moveend event.
         */
        cluster: function(event) {
            if((!event || event.zoomChanged || (event && event.recluster)) && this.features) {
                var resolution = this.layer.map.getResolution();
                if(resolution != this.resolution || !this.clustersExist() || (event && event.recluster)) {
                    this.resolution = resolution;
                    var clusters = [];
                    var feature, clustered, cluster;
                    for(var i=0; i<this.features.length; ++i) {
                        feature = this.features[i];
                        if(feature.geometry) {
                            clustered = false;
                            for(var j=clusters.length-1; j>=0; --j) {
                                cluster = clusters[j];
                                if(this.shouldCluster(cluster, feature)) {
                                    this.addToCluster(cluster, feature);
                                    clustered = true;
                                    break;
                                }
                            }
                            if(!clustered) {
                                clusters.push(this.createCluster(this.features[i]));
                            }
                        }
                    }
                    this.layer.removeAllFeatures();
                    if(clusters.length > 0) {
                        if(this.threshold > 1) {
                            var clone = clusters.slice();
                            clusters = [];
                            var candidate;
                            for(var i=0, len=clone.length; i<len; ++i) {
                                candidate = clone[i];
                                if(candidate.attributes.count < this.threshold) {
                                    Array.prototype.push.apply(clusters, candidate.cluster);
                                } else {
                                    clusters.push(candidate);
                                }
                            }
                        }
                        this.clustering = true;
                        // A legitimate feature addition could occur during this
                        // addFeatures call.  For clustering to behave well, features
                        // should be removed from a layer before requesting a new batch.
                        this.layer.addFeatures(clusters);
                        this.clustering = false;
                    }
                    this.clusters = clusters;
                }
            }
        },
    
        /**
         * Method: recluster
         * User-callable function to recluster features
         * Useful for instances where a clustering attribute (distance, threshold, ...)
         *     has changed
         */
        recluster: function(){
            var event={"recluster":true};
            this.cluster(event);
        },
    
        /**
         * Method: clustersExist
         * Determine whether calculated clusters are already on the layer.
         *
         * Returns:
         * {Boolean} The calculated clusters are already on the layer.
         */
        clustersExist: function() {
            var exist = false;
            if(this.clusters && this.clusters.length > 0 &&
               this.clusters.length == this.layer.features.length) {
                exist = true;
                for(var i=0; i<this.clusters.length; ++i) {
                    if(this.clusters[i] != this.layer.features[i]) {
                        exist = false;
                        break;
                    }
                }
            }
            return exist;
        },
    
        /**
         * Method: shouldCluster
         * Determine whether to include a feature in a given cluster.
         *
         * Parameters:
         * cluster - {<OpenLayers.Feature.Vector>} A cluster.
         * feature - {<OpenLayers.Feature.Vector>} A feature.
         *
         * Returns:
         * {Boolean} The feature should be included in the cluster.
         */
        shouldCluster: function(cluster, feature) {
            var cc = cluster.geometry.getBounds().getCenterLonLat();
            var fc = feature.geometry.getBounds().getCenterLonLat();
            var distance = (
                Math.sqrt(
                    Math.pow((cc.lon - fc.lon), 2) + Math.pow((cc.lat - fc.lat), 2)
                ) / this.resolution
            );
            return (distance <= this.distance);
        },
    
        /**
         * Method: addToCluster
         * Add a feature to a cluster.
         *
         * Parameters:
         * cluster - {<OpenLayers.Feature.Vector>} A cluster.
         * feature - {<OpenLayers.Feature.Vector>} A feature.
         */
        addToCluster: function(cluster, feature) {
            cluster.cluster.push(feature);
            cluster.attributes.count += 1;
        },
    
        /**
         * Method: createCluster
         * Given a feature, create a cluster.
         *
         * Parameters:
         * feature - {<OpenLayers.Feature.Vector>}
         *
         * Returns:
         * {<OpenLayers.Feature.Vector>} A cluster.
         */
        createCluster: function(feature) {
            var center = feature.geometry.getBounds().getCenterLonLat();
            var cluster = new OpenLayers.Feature.Vector(
                new OpenLayers.Geometry.Point(center.lon, center.lat),
                {count: 1}
            );
            cluster.cluster = [feature];
            return cluster;
        },
    
        CLASS_NAME: "OpenLayers.Strategy.Cluster" 
    });
    

    【讨论】:

    • 太好了,这正是我所需要的!几件事...... 1)您可以将其添加到核心 js(cluster.js)中,然后重新构建您自己的自定义 OpenLayers.js 以避免重新实现所有这些。或者.. 2)您应该能够将这些方法添加到类中,而无需重新编码整个类。
    • 好点,@Matt。我首选的方法是将它作为 OpenLayers 的一部分。这是票 #3384,自 2011 年 6 月 23 日起生效。也许它会进入 v2.13... :-)
    【解决方案2】:

    OpenLayers.Strategy.Cluster.cluster 方法仅在它是 zoomechanged 事件或 clusters 对象不存在时才会重新计算。移除集群对象并在集群对象上调用集群。

    var clustering=new OpenLayers.Strategy.Cluster()
    var vectorlayer = new OpenLayers.Layer.Vector('Vectorlayer', {
                                                  strategies: [clustering]
    });
    
    //ADD_LOTS_OF_FEATURES_TO_VECTOR_LAYER
    
    clustering.distance=value;
    //cluster() recalculate only if it is a zoomechanged event or clusters object does not exist.
    clustering.clusters = null; //remove cluster object so that it calculates again.
    clustering.cluster();
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-05-15
      • 2012-11-30
      • 2016-08-23
      • 2020-01-17
      • 1970-01-01
      • 2011-10-16
      • 2012-08-22
      • 2015-01-07
      相关资源
      最近更新 更多