summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorÉtienne Loks <etienne.loks@peacefrogs.net>2012-11-11 13:31:07 +0100
committerÉtienne Loks <etienne.loks@peacefrogs.net>2012-11-11 13:31:07 +0100
commit440b7358ad5e8fd64e9bc7f7e6a56481fa70390f (patch)
tree1942827f2d522de9cf53609e50733b0bd78903ff
parent66847e9beae21798847df96568ea487c0b00b16a (diff)
downloadChimère-440b7358ad5e8fd64e9bc7f7e6a56481fa70390f.tar.bz2
Chimère-440b7358ad5e8fd64e9bc7f7e6a56481fa70390f.zip
JS: Fix clustering redraw and layer z-index after category check
-rw-r--r--chimere/static/chimere/js/clustering.js278
-rw-r--r--chimere/static/chimere/js/jquery.chimere.js8
-rw-r--r--chimere/templates/chimere/blocks/head_chimere.html1
3 files changed, 285 insertions, 2 deletions
diff --git a/chimere/static/chimere/js/clustering.js b/chimere/static/chimere/js/clustering.js
new file mode 100644
index 0000000..b3a142c
--- /dev/null
+++ b/chimere/static/chimere/js/clustering.js
@@ -0,0 +1,278 @@
+/*
+ Add a recluster to Cluster class.
+ probably part of OpenLayers 2.13
+*/
+
+
+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"
+});
diff --git a/chimere/static/chimere/js/jquery.chimere.js b/chimere/static/chimere/js/jquery.chimere.js
index 88e9654..71b0d76 100644
--- a/chimere/static/chimere/js/jquery.chimere.js
+++ b/chimere/static/chimere/js/jquery.chimere.js
@@ -320,11 +320,12 @@ OpenLayers.Layer.MapQuestOSM = OpenLayers.Class(OpenLayers.Layer.XYZ, {
/* Cluster layer */
- var strategy = new OpenLayers.Strategy.Cluster({distance: 20,
+ settings.clustering = new OpenLayers.Strategy.Cluster({
+ distance: 20,
threshold: 3});
settings.layerCluster = new OpenLayers.Layer.Vector("Cluster layer",
{styleMap: new OpenLayers.StyleMap({'default': style}),
- strategies: [strategy]});
+ strategies: [settings.clustering]});
settings.map.addLayer(settings.layerCluster);
var highlightCtrl = new OpenLayers.Control.SelectFeature(
@@ -561,8 +562,10 @@ OpenLayers.Layer.MapQuestOSM = OpenLayers.Class(OpenLayers.Layer.XYZ, {
if (settings.enable_clustering){
settings.layerCluster.addFeatures(
settings.cluster_array);
+ settings.clustering.recluster();
methods.cleanCluster();
}
+ settings.map.resetLayersZIndex();
},
error: function (data) {
settings.layerMarkers.clearMarkers();
@@ -721,6 +724,7 @@ OpenLayers.Layer.MapQuestOSM = OpenLayers.Class(OpenLayers.Layer.XYZ, {
settings.layerMarkers.markers[j].display(true);
}
}
+ settings.layerCluster.refresh({force:true});
},
/*
* Put a marker on the map
diff --git a/chimere/templates/chimere/blocks/head_chimere.html b/chimere/templates/chimere/blocks/head_chimere.html
index 29a1be4..6d8ed4a 100644
--- a/chimere/templates/chimere/blocks/head_chimere.html
+++ b/chimere/templates/chimere/blocks/head_chimere.html
@@ -3,6 +3,7 @@
{% for js_url in OSM_JS_URLS %}
<script src="{{ js_url }}"></script>{% endfor %}
{% if routing %}<script src="{{ STATIC_URL }}chimere/js/routing-widget.js"></script>{% endif %}
+<script src="{{ STATIC_URL }}chimere/js/clustering.js"></script>
<script src="{{ STATIC_URL }}chimere/js/jquery.chimere.js"></script>
<script type="text/javascript">
/* Global variables */