2011-06-23 67 views
6

所以,我有一些聚集在OpenLayers中的项目。OpenLayers群集重新计算

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

我更改了项目的属性。

如何获得群集策略来重新计算群集?

回答

13

好吧,事实证明,这个功能在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" 
}); 
+0

非常好,这正是我所需要的!几件事情...... 1)您可以将其添加到核心js(cluster.js),然后重新构建您自己的自定义OpenLayers.js以避免必须重新实现所有这些。或者.. 2)您可以将这些方法添加到课程中,而不必重新编写整个课程。 –

+0

好点,@Matt。我的首选方法是将它作为OpenLayers的一部分。这是门票#3384,自2011-06-23开始运行。也许它会变成v2.13 ... :-) – Richard

+0

如果我可以在票上投票,我会做;) –

1

OpenLayers.Strategy.Cluster.cluster方法仅重新计算,如果它是一个zoomechanged事件或簇对象不存在。删除群集对象并调用群集对象上的群集。

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();