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:

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.

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.

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.

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.

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.

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:

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:

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 is OpenLayers.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.

33 Responses

  1. Pol August 22, 2012 / 23:01

    AMAZING !!!! I love it :)

    Thanks Antonio !

  2. andy August 29, 2012 / 17:16

    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.

  3. Sebastian September 7, 2012 / 16:30

    Excelent job!! thanks

  4. Rakesh November 23, 2012 / 17:48

    Many many thanks Antonio.. We love you..:)

  5. Rakesh November 27, 2012 / 16:19

    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.

    Thank you so much in advance.

    Best Regard's
    Rakesh

    • asantiago November 27, 2012 / 20:48

      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 count property and a features property which holds references to your real features.
      If you work with the SelectFeature control and register an event listener in the vector layer for the featureselected event like this:

      The event object contains a feature attribute 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.

  6. Rakesh November 27, 2012 / 22:52

    Dear Antonio..:) Tone of thanks for your super fast response and very good explanation..;)

  7. Rakesh November 28, 2012 / 15:32

    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.

  8. Rakesh November 29, 2012 / 11:20

    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.

  9. Rakesh November 29, 2012 / 11:31

    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

    • asantiago November 30, 2012 / 08:05

      Thanks Rakesh. Now I need to get some free time to fix that issues :)

  10. Reynaldo November 29, 2012 / 22:34

    Antonio un super Trabajo, es excelente!!!!!!!

  11. Rakesh December 5, 2012 / 18:44

    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.

    Thanks in Advance..;)

    • asantiago December 5, 2012 / 19:44

      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

  12. Rakesh December 5, 2012 / 22:11

    Many many thanks Antonio..:)

  13. Mihkel January 17, 2013 / 13:59

    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.

  14. Lau January 20, 2013 / 23:10

    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.

  15. Moh Aziz March 27, 2013 / 16:00

    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:

    thanks for your help

    • asantiago March 27, 2013 / 19:32

      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.

  16. Johnny October 23, 2013 / 06:40

    Pretty good extension!!! but seems like the markers flick a lot when we pan the map. Is it by design?

  17. Alexander December 2, 2013 / 11:50

    Hi, nice code and very good idea.
    When im trying out, i don’t get the count of the featuers in the cluster-presentation on the map.
    What should i do that we can see the count of featuers in the cluster?

    • asantiago December 2, 2013 / 22:04

      Thanks Alexander,
      which browser are you using?
      Can you check if your browser has canvas and/or SVG support?

      The sample works fine for me on chrome and FF.

  18. Ashish Rathod January 29, 2014 / 08:26

    Hello Antonio,

    I have download sample create sample demo in my local host but world_cities.json file neither get data and nor display layer in map so please let me know about that issue.

    • asantiago January 29, 2014 / 09:32

      Your explanation is a bit vage. Are you running the code within a web server? The JSON file is loaded via AJAX.

      • Ashish Rathod January 29, 2014 / 11:11

        Hello,

        Yes i have work with web server ..

        • asantiago January 29, 2014 / 11:51

          Load the demo page and see the source code. There is no mistery. I created a vector layer, which loads data from the JSON file at the same directory:

          Check your browser console output.

          • Ashish Rathod January 29, 2014 / 12:22

            Hello,

            Failed to load resource: the server responded with a status of 404 (Not Found)
            error found when i run code so any idea about that.

            Json file not found when i run project with my local host so please help me if u have any idea ..

            Thanks & Regards,
            Ashish Rathod

  19. Rakesh May 23, 2014 / 15:49

    Hallo again,

    I am Rakesh. We talked many years ago. I have come across AnimatedCluser.js file, unfortunatly it is not working with IE8. could you please provide me more information >

    Thank you Antonio..:)

  20. Alexander November 4, 2014 / 18:56

    This Script is great have anyone tested it with new OpenLayers 3?

    • asantiago November 5, 2014 / 07:49

      Nope, it is incompatible. OL2 and OL3 have completely different API.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">