Animated marker cluster strategy for OpenLayers
Yes, this posts talks about my implementation of a cluster strategy that animates clusters when changing the zoom level. So, if you are one of those don’t like to read the blog posts from others and like the action, you can go directly to see AnimatedCluster demo page. or to the github repository to see the source code.
Read this post if you want to know how the sample works.
Next, you can find a descriptions on how the implementation works. I have tried to be has much OpenLayers compliant as possible, without using any other framework, library or classes outside the OpenLayers and following its design of concepts.
It is necessary to make a short introduction of concepts about OpenLayers, like vector layers and strategies, so the reader can understand more in depth the implementation.
NOTE: It was only tested with OpenLayers 2.11 !!!
Some background
Many time ago (at the end of 2011 year) I saw a great question in stackoverflow about How to Create Animated Cluster Markers in OpenLayers/Leaflet? and, also, a similar one about OpenLayers, nice marker clustering.
Everybody knows a picture is worth a thousand words and when working with data visualization this is not an exception. It is not only important the look but the feel and having a nice animation of features while changing the zoom level is something visually funny and attractive.
On that days, I was completely busy involved on writing the book OpenLayers Cookbook. Initially I think to put hands on and try to implement a solution in OpenLayers, so it can serve as another recipe for the book. Unfortunately, I had no much time to spent in that challenge and I forget it to concentrate writing the book with a bunch of more practical recipes.
A month ago, Dave Leaver answers in the forum pointing to its awesome clustered marker implementation for Leaflet, which I must say is terrific.
Fortunately for the community, the implementation animated cluster for Leaflet was like a kick on my ass, it was what I need to start the challenge
Last week I start working in an implementation for OpenLayers. Spending my few nigh time on it and after some tries and errors, today I’m able to publish the source code and documentation in addition to this post to the masses.
A short Vector Layer class description
In OpenLayers, when you want to render any feature within a map yo need to previously create a vector layer:
var vector = new OpenLayers.Layer.Vector("Features");
Vector layers are one of the most complex and powerful kind of layers within the OpenLayers and because of this there is a great ecosystem of classes related with the vector layers:
Approximated conceptual model
Features and Geometries
A vector layer is nothing more than a container for features, which are real world observations: a city, a river, a mountain peak, a country, a continent, … all these are features. In addition, a feature can have a set of attributes like: population, length, height, etc.

Geometries hierarchy
Each feature can be visualized in a different way and this way is determined by a geometry associated with the feature. Points, LineString, Polygons, .. are different kinds of geometries we can use to visualize a feature.
// This code adds a point feature to a vector layer var pointGeometry = new OpenLayers.Geometry.Point(px, py); var pointFeature = new OpenLayers.Feature.Vector(pointGeometry); vector.push([pointFeatures]);
Renderers and Styles
Browsers can have support for different technologies: Canvas, SVG, etc. So to make the process of draw geometries in the map independent from the vector layer, OpenLayers has the concept of renderer. A renderer is a special kind of class responsible to draw a geometry using a concrete technology.
Next code creates a new vector layer where we are specifying OpenLayers tries to render features using Canvas capabilities at first instance or otherwise using SVG technology.
var vector = new OpenLayers.Layer.Vector("Features", {
renderers: ['Canvas','SVG']
});
In addition, a feature (or the vector layer itself) can have a style associated that is used by the renderer to draw the geometry that represents a feature. This way we can have cities rendered as blue points with radius 10px and mountain peaks rendered as brown points with radius 7px.
Protocols and Formats
A vector layer can have a protocol class associated with it. Protocols are special classes that knows how to read/write features from different sources and uses Format classes to read/write data in a specific format. For example: we can load features using the HTTP protocol from a GeoJSON or KML file.
Protocols and format classes makes the vector layer independent from the data source.
var vector = new OpenLayers.Layer.Vector("Features", {
protocol: new OpenLayers.Protocol.HTTP({
url: "world_cities.json",
format: new OpenLayers.Format.GeoJSON()
})
});
Previous code creates a new vector layer that load data via HTTP from a GeoJSON format file.
Strategies
A vector layer can have one or more strategies attached to it. An strategy is a kind of class that, once activated, makes things on the vector layer without layer knows what is happening.
Examples of strategies are:
OpenLayers.Strategy.Fixed, which loads the content of a data source attached to the vector layer only once. This is usually used to load a data file in a vector layer, because it is only required to be loaded once.OpenLayers.Strategy.Box, which loads content from a data source each time the bounding box of the viewport changed. This strategy is useful for vector layers that loads content from a WFS server and needs to update its contents every time the BBOX changes.
var vector = new OpenLayers.Layer.Vector("Features", {
protocol: new OpenLayers.Protocol.HTTP({
url: "world_cities.json",
format: new OpenLayers.Format.GeoJSON()
}),
strategies: [new OpenLayers.Strategy.Fixed()]
});
The previous code creates a vector layer that load data from a GeoJSON file. The Fixed strategy allows to load the file content once and only once, no mater if viewport BBOX changes.
The cluster strategy
When a vector layer contains tons of features and, they are rendered as points, an ugly effect can appear in our map. Depending on the zoom level and the point radius the points can be rendered overlapping each others.
One of the most useful strategies offered by OpenLayers is the OpenLayers.Strategy.Cluster. Given the set of features withint a layer and a zoom level, this strategy computes the distance among the features and clusters them so no cluster overlaps any other.
Next figures show a sample without and with applying the cluster strategy to a set of features within a vector layer:

Without cluster strategy

With cluster strategy
The code required for the second figure is:
var vector = new OpenLayers.Layer.Vector("Features", {
protocol: new OpenLayers.Protocol.HTTP({
url: "world_cities.json",
format: new OpenLayers.Format.GeoJSON()
}),
strategies: [
new OpenLayers.Strategy.Fixed(),
new OpenLayers.Strategy.Cluster()
]
});
How the cluster strategy works?
The cluster strategy instance holds a reference to the vector layers features. In addition each time the zoom level changes, it computes a set of clusters each one containing some number of features.
The clusters are nothing more than point geometry features with a new count attribute (an holding references to the features it contains).
In addition the cluster strategy, removes the “original” set of features from the vector layers and adds the computed clusters to it.
So (IMO)… What is the problem with the cluster strategy?
Really there is no problem with cluster strategy, it works like a charm. From my point of view the problem is related with the user experience, more concretely to the look and feel.
IMO, the ugly effect with cluster strategy is done when the user changes the zoom level of the map. The clusters are computed again and they are drawn fine but without a nice animation that indicates the user the new cluster division or merge.
Once can think no matter the look and feel of a bunch features on the screen, but I think if we can have a good functionality with a nice experience that is twice better than only as good functionality.
How to use the AnimatedCluster
The OpenLayers.Strategy.AnimatedCluster class is a subclass of OpenLayers.Strategy.Cluster class so it inherits all the properties and extends some features.
The usage is as easy as the cluster strategy. When you create a vector layer you need to pass an instance of OpenLayers.Strategy.AnimatedCluster in the strategies property:
var vector = new OpenLayers.Layer.Vector("Features", {
...
strategies: [
new OpenLayers.Strategy.Fixed(),
new OpenLayers.Strategy.AnimatedCluster({
distance: 45,
animationMethod: OpenLayers.Easing.Expo.easeOut,
animationDuration: 10
})
],
...
});
Properties
In addition to the inherited properties distance and threshold the AnimatedCluster offers a two new ones:
animationMethod: The tweening method to use. By default if isOpenLayers.Easing.Expo.easeOut.animationDuration: The duration of the tween in number of steps. By default it is 20.
How the AnimatedCluster works?
Like its parent class the animated cluster strategy holds a reference to the features of the vector layer and each the zoom level changes the next steps takes place:
- Store a reference to the previous computed cluster (that related with the previous zoom level we come from)
- Compute the new cluster
- Start a tween to animate from the previous to the new cluster positions.
These means animated cluster makes use of an extra array of clustered features (the previous one) but its size is relatively small, so there is no performance problem. In addition and, in the same way the cluster strategy does, the animated cluster only the computes the new cluster
The animation is a bit cumbersome. If we zoom in the view, it means we go to a level with more clusters than the previous one, that is, a cluster at level N becomes M clusters at level N+1.

The vice-versa occurs for zoom out actions, we go to a new level with less clusters than the previous one, that is, M clusters at level N becomes 1 cluster at level N-1.
Things to take into account with the AnimatedCluster
There are two important things to understand and take into account when working with the AnimateCluster.
First, the animation is made using the OpenLayers.Tween class OpenLayers offers. This class is used to make animation when map panning and offers basic actions to make animations.
Unfortunately, the class does not allows specify the animation duration in second but in steps. You must know that the OpenLayers.Tween wait 10 milliseconds between steps (see documentation for the OpenLayers.Tween.INTERVAL property), so a duration of 50 steps means the animation is made in 500 milliseconds.
Second, each animation steps implies to redraw the vector layer again, to refresh the new position of the clusters. As you can see this has a direct performance impact.
Conclusions
To be the first implementation I proudly enough of the work but I am aware of the improvements it can be made.
Any help will be appreciated.



AMAZING !!!! I love it
Thanks Antonio !
this is very cool stuff. thanks for sharing, will have to try it out in a recent project where we have tons of tightly clustered points, but from a distance it just looks like one big ugly blog.
Excelent job!! thanks
Many many thanks Antonio.. We love you..:)
Hello Antonio,
I have used animated javascript, it is working fine. however, it is giving me wrong feature when I select it on the screen. could you please provide me more information regarding this issue.
console.log('**** Vector Id:'+vector.id); selector = new OpenLayers.Control.SelectFeature(vector,{ clickout: true, toggle: false, multiple: true, hover: true, callbacks: { 'over':function(feature){ console.log('**** Attribute :'+vector.features); for (var i=0; i<vector.features.length; i++) { var id = vector.features[i].id; console.log("id: "+id); } //mouseOverFeaturePopup(feature); }, 'out':function(feature){ mouseOverFeatureRemovePopup(feature); }, 'click':function(feature){ var url = feature.attributes.hotelUrl; window.location = url; return false; } } });Thank you so much in advance.
Best Regard's
Rakesh
Hi Rakesh,
remember when you work with cluster strategy OpenLayers create group your features into clusters features, that is, the cluster strategy creates new features that contains a
countproperty and afeaturesproperty which holds references to your real features.If you work with the
SelectFeaturecontrol and register an event listener in the vector layer for thefeatureselectedevent like this:vector1.events.register('featureselected', null, function(event) { #code here });The
eventobject contains afeatureattribute which is really the cluster feature that contains all your features. That is, yo get the features contained in the cluster you must write:var myFeatyures = event.feature.cluster.features;Which returns an array with all the features in that cluster.
It is your responsibility to get right feature within the set.
Cheers.
Dear Antonio..:) Tone of thanks for your super fast response and very good explanation..;)
Dear Antonio,
Do you have any idea about below error, which is appearing while zoom in n zoom out of mouse click. If you want, I can also send you code snippet. Thank you so much in advance Antonio.:)
Uncaught TypeError: Cannot read property ‘destx’ of undefined AnimatedCluster.js:313
OpenLayers.Strategy.AnimatedCluster.OpenLayers.Class.animate AnimatedCluster.js:313
(anonymous function) OpenLayers.js:62
OpenLayers.Tween.OpenLayers.Class.play OpenLayers.js:160
(anonymous function) OpenLayers.js:62
e.(anonymous function)
Have a nice time.
Dear Antonio,
somehow, I am missing some important feature while zoom in zoom out from animated cluster. I think, below error could be responsible for that.
I wish, you could throw some lights on this issue. Many many thanks.
Dear Antonio,
I have tested ‘AnimatedCluster demo page’ from your web page, it is also giving the same error, which is been produced at my page. Error generation Steps are given.
(1) Zoom in up to maximum level
(2) then panning of map from one side to another side n try to find feature which was always there, during clustered being formed. somehow, we are missing that important features while panning of map.
During this step, you would be able to see the same error on java scrip console. I wish, I have explained well.
Best Regard’s
Rakesh
Thanks Rakesh. Now I need to get some free time to fix that issues
Antonio un super Trabajo, es excelente!!!!!!!
Hello Antonio,
Do you have any idea, how someone could remove the features from the vector. I am trying simple way but because of cluster features, they are not deleted.
console.log('***** Length :'+vector.features.size()); console.log('***** Selected Feature :'+vector.selectedFeatures); for(var i=0;i<vector.features.size();i=i+1) { vector.removeFeatures(vector.features[i],false); console.log('***** Cluster :'+vector.features[i].cluster.size()); for(key in vector.features[i].cluster) { console.log('**** Key :'+key); } } vector.destroyFeatures(vector.features); map.addLayer(vector);Thanks in Advance..;)
Hi Rakesh,
I have made some tests and it seems an inherent issue from the cluster strategy.
The cluster strategy holds a reference to the original vector layer features and changes it by the computed clusters.
A way to remove features is get a cluster strategy reference and remove features on it, for examples:
I think it is an ugly way to remove features, because you need to find the desired OpenLayers.Feature.Vector feature withint the array, but at least you can empty the feature set easily.
Kind regards
Many many thanks Antonio..:)
Hi! Impressive with couple drawbacks.
1. It handles only these features that are on viewport. Excellent solution if you handle large amount of features on map, but it dosn’t handle map moves, so features outside of viewport isn’t rendered until you change zoom.
2. It dosn’t take account threshold bigger than 1, so it means that all features have to be clusters, no reguler features at all.
Otherwise very nice class.
Thanks for explaining this method, very attractive way of presenting point data!
I used your approach and applied a selectfeature control to the layer as well.
Could you explain how to get a pointer cursor when hovering over a cluster?
I tried to include cursor: “pointer” in all three syle rule symbolizers of your example. However, this approach does not work as expected.
Check the possible set of properties you can pass within the style for a feature: http://dev.openlayers.org/docs/files/OpenLayers/Feature/Vector-js.html#OpenLayers.Feature.Vector.OpenLayers.Feature.Vector.style. There is a
cursorproperty to change the cursos icon.Yes, but there isn’t cursor for label, so specified cursor works as long as you move your cursor over label.
Dear Antonio, the animated cluster is amazing. I have a question:
I want to include markers so i need to make each feature a marker. Something like the Leaflet marker clusters BUT i do not need that much animation. My markers can be simple and only displayed as a popup once clicked on. Here is some code i added to your cluster code but it does not work:
var switcherControl = new OpenLayers.Control.LayerSwitcher(); map.addControl(switcherControl); switcherControl.maximizeControl(); var f = new OpenLayers.Feature.Vector( new OpenLayers.Geometry.Point(lonlat.lon, lonlat.lat), { description: 'test' + i } ); features.push(f); controls = { selector: new OpenLayers.Control.SelectFeature(vectorLayer, { onSelect: createPopup, onUnselect: destroyPopup }) }; map.addControl(controls['selector']); controls['selector'].activate(); function createPopup(feature) { feature.popup = new OpenLayers.Popup.FramedCloud("pop", feature.geometry.getBounds().getCenterLonLat(), null, '' + feature.attributes.description + '', null, true, function () { controls['selector'].unselectAll(); } ); map.addPopup(feature.popup); } function destroyPopup(feature) { feature.popup.destroy(); feature.popup = null; }thanks for your help
Hi Moh,
thanks for your comment.
Please enter an issue in the github system (https://github.com/acanimal/AnimatedCluster/issues?state=open)
I’m completely busy and have almost no time to review the AnimatedCluster code, but the project has a couple of contributors they can help you.
I will try to take a look to your problem ASAP.
Cheers.