Browse files

add an Extend Cluster Strategy example, provided by Marc Jansen, no f…

…unctional change to the library, r=me (closes #2865)

git-svn-id: http://svn.openlayers.org/trunk/openlayers@10811 dc9f47b5-9b13-0410-9fdd-eb0c1a62fdaf
  • Loading branch information...
1 parent 26d20c1 commit ee9c3e76af3f12828320d92a95416d6e47188adf Éric Lemoine committed Oct 7, 2010
Showing with 369 additions and 0 deletions.
  1. +122 −0 examples/strategy-cluster-extended.html
  2. +247 −0 examples/strategy-cluster-extended.js
View
122 examples/strategy-cluster-extended.html
@@ -0,0 +1,122 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Extended clustering example</title>
+ <link rel="stylesheet" href="../theme/default/style.css" type="text/css">
+ <link rel="stylesheet" href="style.css" type="text/css">
+ <style type="text/css">
+ label {
+ cursor: pointer
+ }
+
+ #wrap {
+ width: 925px;
+ margin: 10px;
+ }
+
+ #strategy-chooser, #generalinfo, #info {
+ width: 400px;
+ padding: 0;
+ float: right;
+ clear: right;
+ margin-bottom: 4px;
+ }
+
+ #map {
+ float: left;
+ }
+ </style>
+ </head>
+ <body>
+ <h1 id="title">Extended clustering</h1>
+ <div id="tags">
+ cluster, advanced
+ </div>
+ <p id="shortdesc">
+ Shows the usage of custom classes for a fine grained control about
+ the clustering behaviour.
+ </p>
+ <div id="wrap">
+ <div id="map" class="smallmap">
+ </div>
+ <div id="strategy-chooser">
+ <p>
+ Select the desired clustering strategy:
+ </p>
+ <label>
+ <input type="radio" name="strategy" value="none" id="no-strategy" checked="checked">No strategy
+ </label>
+ <br/>
+ <label>
+ <input type="radio" name="strategy" value="cluster" id="cluster-strategy">Simple cluster-strategy
+ </label>
+ <br/>
+ <label>
+ <input type="radio" name="strategy" value="attribute-cluster" id="attributive-cluster-strategy">Attributive cluster-strategy
+ </label>
+ <br/>
+ <label>
+ <input type="radio" name="strategy" value="rule-cluster" id="rulebased-cluster-strategy">Rulebased cluster-strategy
+ </label>
+ </div>
+ <div id="generalinfo">
+ </div>
+ <div id="info">
+ </div>
+ </div>
+ <div id="docs" style="clear: both; padding-top: 10px">
+ <p>
+ The vectorlayer in this example contains random data with an
+ attribute "clazz" that can take the values 1, 2, 3 and 4. The
+ features with clazz = 4 are considered more important than the
+ others.
+ </p>
+ <p>
+ The radiobuttons on the right of the map control the
+ cluster strategy to be applied to the features.
+ </p>
+ <ul>
+ <li>
+ <strong>No strategy</strong>
+ means that all features are
+ rendered, no clustering shall be applied
+ </li>
+ <li>
+ <strong>Simple cluster-strategy</strong>
+ applies the cluster
+ strategy with default options to the layer. You should notice
+ that many of the important features with clazz = 4 are getting
+ lost, since clustering happens regardless of feature attributes
+ </li>
+ <li>
+ <strong>Attributive cluster-strategy</strong>
+ uses a
+ customized cluster strategy. This strategy is configured to
+ cluster features of the same clazz only. You should be able to see all
+ red points (clazz = 4) even though the data is clustered. A
+ cluster now contains only features of the same clazz.
+ </li>
+ <li>
+ <strong>Rulebased cluster-strategy</strong>
+ uses another
+ customized cluster strategy. This strategy is configured to
+ cluster features that follow a certain rule only. In this case only
+ features with a clazz different from 4 are considered as
+ candidates for clustering. That means that usually you have fewer
+ clusters on the map, yet all with clazz = 4 are easily
+ distinguishable
+ </li>
+ </ul>
+ <p>
+ Hover over the features to get a short infomation about the
+ feature or cluster of features.
+ </p>
+ </div>
+ <p>
+ View the <a href="strategy-cluster-extended.js" target="_blank">strategy-cluster-extended.js</a>
+ source to see how this is done.
+ </p>
+ <script type="text/javascript" src="../lib/OpenLayers.js"></script>
+ <script type="text/javascript" src="strategy-cluster-extended.js"></script>
+ </body>
+</html>
View
247 examples/strategy-cluster-extended.js
@@ -0,0 +1,247 @@
+/**
+ * Class: OpenLayers.Strategy.AttributeCluster
+ * Strategy for vector feature clustering based on feature attributes.
+ *
+ * Inherits from:
+ * - <OpenLayers.Strategy.Cluster>
+ */
+OpenLayers.Strategy.AttributeCluster = OpenLayers.Class(OpenLayers.Strategy.Cluster, {
+ /**
+ * the attribute to use for comparison
+ */
+ attribute: null,
+ /**
+ * 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_attrval = cluster.cluster[0].attributes[this.attribute];
+ var fc_attrval = feature.attributes[this.attribute];
+ var superProto = OpenLayers.Strategy.Cluster.prototype;
+ return cc_attrval === fc_attrval &&
+ superProto.shouldCluster.apply(this, arguments);
+ },
+ CLASS_NAME: "OpenLayers.Strategy.AttributeCluster"
+});
+
+/**
+ * Class: OpenLayers.Strategy.RuleCluster
+ * Strategy for vector feature clustering according to a given rule.
+ *
+ * Inherits from:
+ * - <OpenLayers.Strategy.Cluster>
+ */
+OpenLayers.Strategy.RuleCluster = OpenLayers.Class(OpenLayers.Strategy.Cluster, {
+ /**
+ * the rule to use for comparison
+ */
+ rule: null,
+ /**
+ * 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 superProto = OpenLayers.Strategy.Cluster.prototype;
+ return this.rule.evaluate(cluster.cluster[0]) &&
+ this.rule.evaluate(feature) &&
+ superProto.shouldCluster.apply(this, arguments);
+ },
+ CLASS_NAME: "OpenLayers.Strategy.RuleCluster"
+});
+
+
+// global variables
+var map, vectorlayer, features, stylemap, select;
+
+// wrap the instanciation code in an anonymous function that gets executed
+// immeadeately
+(function(){
+
+ // The function that gets called on feature selection: shows information
+ // about the feature/cluser in a div on the page
+ var showInformation = function(evt){
+ var feature = evt.feature;
+ var info = 'Last hovered feature:<br>';
+ if (feature.cluster) {
+ info += '&nbsp;&nbsp;Cluster of ' + feature.attributes.count + ' features:';
+ var clazzes = {
+ '1': 0,
+ '2': 0,
+ '3': 0,
+ '4': 0
+ };
+ for (var i = 0; i < feature.attributes.count; i++) {
+ var feat = feature.cluster[i];
+ clazzes[feat.attributes.clazz]++;
+ }
+ for (var j=1; j<=4; j++) {
+ var plural_s = (clazzes[j] !== 1) ? 's' : '';
+ info += '<br>&nbsp;&nbsp;&nbsp;&nbsp;&bull;&nbsp;clazz ' + j + ': ' + clazzes[j] + ' feature' + plural_s;
+ }
+ } else {
+ info += '&nbsp;&nbsp;Single feature of clazz = ' + feature.attributes.clazz;
+ }
+ $('info').innerHTML = info;
+ };
+
+ // The function that gets called on feature selection. Shows information
+ // about the number of "points" on the map.
+ var updateGeneralInformation = function() {
+ var info = 'Currently ' + vectorlayer.features.length + ' points are shown on the map.';
+ $('generalinfo').innerHTML = info;
+ };
+
+ // instanciate the map
+ map = new OpenLayers.Map("map");
+
+ // background WMS
+ var ol_wms = new OpenLayers.Layer.WMS("OpenLayers WMS", "http://vmap0.tiles.osgeo.org/wms/vmap0", {
+ layers: "basic"
+ });
+
+ // context to style the vectorlayer
+ var context = {
+ getColor: function(feature){
+ var color = '#aaaaaa';
+ if (feature.attributes.clazz && feature.attributes.clazz === 4) {
+ color = '#ee0000';
+ } else if(feature.cluster) {
+ var onlyFour = true;
+ for (var i = 0; i < feature.cluster.length; i++) {
+ if (onlyFour && feature.cluster[i].attributes.clazz !== 4) {
+ onlyFour = false;
+ }
+ }
+ if (onlyFour === true) {
+ color = '#ee0000';
+ }
+ }
+ return color;
+ }
+ };
+
+ // style the vectorlayer
+ stylemap = new OpenLayers.StyleMap({
+ 'default': new OpenLayers.Style({
+ pointRadius: 5,
+ fillColor: "${getColor}",
+ fillOpacity: 0.7,
+ strokeColor: "#666666",
+ strokeWidth: 1,
+ strokeOpacity: 1,
+ graphicZIndex: 1
+ }, {
+ context: context
+ }),
+ 'select' : new OpenLayers.Style({
+ pointRadius: 5,
+ fillColor: "#ffff00",
+ fillOpacity: 1,
+ strokeColor: "#666666",
+ strokeWidth: 1,
+ strokeOpacity: 1,
+ graphicZIndex: 2
+ })
+ });
+
+ // the vectorlayer
+ vectorlayer = new OpenLayers.Layer.Vector('Vectorlayer', {styleMap: stylemap, strategies: []});
+
+ // the select control
+ select = new OpenLayers.Control.SelectFeature(
+ vectorlayer, {hover: true}
+ );
+ map.addControl(select);
+ select.activate();
+ vectorlayer.events.on({"featureselected": showInformation});
+
+ map.addLayers([ol_wms, vectorlayer]);
+ map.addControl(new OpenLayers.Control.LayerSwitcher());
+ map.zoomToMaxExtent();
+
+ features = [];
+ // adding lots of features:
+ for (var i = 0; i < 700; i++) {
+ var r1 = Math.random();
+ var r2 = Math.random();
+ var r3 = Math.random();
+ var r4 = Math.random();
+ var px = r1 * 180 * ((r2 < 0.5) ? -1 : 1);
+ var py = r3 * 90 * ((r4 < 0.5) ? -1 : 1);
+ var p = new OpenLayers.Geometry.Point(px, py);
+ var clazz = (i % 10 === 0) ? 4 : Math.ceil(r4 * 3);
+ var f = new OpenLayers.Feature.Vector(p, {clazz: clazz});
+ features.push(f);
+ }
+ vectorlayer.addFeatures(features);
+ updateGeneralInformation();
+
+ // the behaviour and methods for the radioboxes
+ var changeStrategy = function() {
+ var strategies = [];
+ // this is the checkbox
+ switch(this.value) {
+ case 'cluster':
+ // standard clustering
+ strategies.push(new OpenLayers.Strategy.Cluster());
+ break;
+ case 'attribute-cluster':
+ // use the custom class: only cluster features of the same clazz
+ strategies.push(new OpenLayers.Strategy.AttributeCluster({
+ attribute:'clazz'
+ }));
+ break;
+ case 'rule-cluster':
+ // use the custom class: only cluster features that have a
+ // clazz smaller than 4
+ strategies.push(new OpenLayers.Strategy.RuleCluster({
+ rule: new OpenLayers.Rule({
+ filter: new OpenLayers.Filter.Comparison({
+ type: OpenLayers.Filter.Comparison.LESS_THAN,
+ property: "clazz",
+ value: 4
+ })
+ })
+ }));
+ break;
+ }
+ // remove layer and control
+ map.removeLayer(vectorlayer);
+ map.removeControl(select);
+ // rebuild layer
+ vectorlayer = new OpenLayers.Layer.Vector('Vectorlayer', {styleMap: stylemap, strategies: strategies});
+ map.addLayer( vectorlayer );
+ vectorlayer.addFeatures(features);
+ // rebuild select control
+ select = new OpenLayers.Control.SelectFeature(
+ vectorlayer, {hover: true}
+ );
+ map.addControl(select);
+ select.activate();
+ vectorlayer.events.on({"featureselected": showInformation});
+ // update meta information
+ updateGeneralInformation();
+ };
+ // bind the behviour to the radios
+ var inputs = document.getElementsByTagName('input');
+ for( var cnt = 0; cnt < inputs.length; cnt++) {
+ var input = inputs[cnt];
+ if (input.name === 'strategy') {
+ input.onclick = changeStrategy;
+ }
+ }
+})();

0 comments on commit ee9c3e7

Please sign in to comment.