git-svn-id: http://svn.openlayers.org/branches/openlayers/2.7@8012 dc9f47b5-9b13-0410-9fdd-eb0c1a62fdaf
262 lines
7.8 KiB
JavaScript
262 lines
7.8 KiB
JavaScript
/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
|
|
* license. See http://svn.openlayers.org/trunk/openlayers/license.txt for the
|
|
* full text of the license. */
|
|
|
|
/**
|
|
* @requires OpenLayers/Strategy.js
|
|
*/
|
|
|
|
/**
|
|
* Class: OpenLayers.Strategy.Cluster
|
|
* Strategy for vector feature clustering.
|
|
*
|
|
* Inherits from:
|
|
* - <OpenLayers.Strategy>
|
|
*/
|
|
OpenLayers.Strategy.Cluster = OpenLayers.Class(OpenLayers.Strategy, {
|
|
|
|
/**
|
|
* Property: layer
|
|
* {<OpenLayers.Layer.Vector>} The layer that this strategy is assigned to.
|
|
*/
|
|
layer: null,
|
|
|
|
/**
|
|
* APIProperty: distance
|
|
* {Integer} Pixel distance between features that should be considered a
|
|
* single cluster. Default is 20 pixels.
|
|
*/
|
|
distance: 20,
|
|
|
|
/**
|
|
* 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.
|
|
*/
|
|
initialize: function(options) {
|
|
OpenLayers.Strategy.prototype.initialize.apply(this, [options]);
|
|
},
|
|
|
|
/**
|
|
* 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,
|
|
scope: this
|
|
});
|
|
this.layer.map.events.on({"zoomend": 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,
|
|
scope: this
|
|
});
|
|
this.layer.map.events.un({"zoomend": 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 layer 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. This destroys features, assuming
|
|
* nothing else has a reference.
|
|
*/
|
|
clearCache: function() {
|
|
if(this.features) {
|
|
for(var i=0; i<this.features.length; ++i) {
|
|
this.features[i].destroy();
|
|
}
|
|
}
|
|
this.features = null;
|
|
},
|
|
|
|
/**
|
|
* Method: cluster
|
|
* Cluster features based on some threshold distance.
|
|
*/
|
|
cluster: function() {
|
|
if(this.features) {
|
|
var resolution = this.layer.getResolution();
|
|
if(resolution != this.resolution || !this.clustersExist()) {
|
|
this.resolution = resolution;
|
|
var clusters = [];
|
|
var feature, clustered, cluster;
|
|
for(var i=0; i<this.features.length; ++i) {
|
|
feature = this.features[i];
|
|
clustered = false;
|
|
for(var j=0; j<clusters.length; ++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.destroyFeatures();
|
|
if(clusters.length > 0) {
|
|
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: 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"
|
|
});
|