So google maps v3 has been released for a while now. In fact, it seems to be a good year and a half if this blog post
has any credibility :p Yet I've not taken the time to play around with it.
But last week I was asked to create a map for schaatsen.nl
with a few dots. So I figured a marker cluster manager would be quite useful. And I thought upgrading my existing script
would be done quickly. Bad judgment call.
Anyways, I used half this weekend to rewrite the script so it would work with v3. First of all, here's the result. A working marker cluster manager for google maps v3. The rest of this blog post will explain the pains I went through.
First of all, my code was bloated to heck. I mean, factories? I can clearly recollect which book I had read prior to writing that piece of junk. Factory patterns probably looked good at some point, but my full rewrite of this script will not have them so explicitly. They over complicate the code for the end user. And myself.
It wasn't long before I hit a snag at gmap's end. It's no longer possible to simply translate a point to a latlng and back. The v3 api
simply doesn't have the method. It has the old methods on a
object, but it didn't seem to be the intended way and there was no
parameter, meaning you'd have to add one object for every zoom level. Note that there are 18 by default. Nice.
So after some searching on the group
I found a post explaining it. I wrote two functions and posted it in my notes
, to be never forgotten again.
The next problem was a little bigger. But let me first explain how the cluster manager works.
Markers are basically just images on a separate layer from the map. When two of these images overlap, they obscure each other, effectively blocking access to one or the other. In certain applications, it's very common to have lot's of markers on or near the same position. Like zoomed out on world level, all the points from the same (small) country will be very near to each other. Large markers are more likely to block other markers. Writing a blog post from the same location every time will put the marker on the same position every time (a reason blogs don't usually have maps ;)). Etc.
So the solution is using a cluster manager, which will detect these overlapping markers and group them together (creating a cluster). The core of this mechanism is really simple, checking the bounds for the image used for each marker and checking whether they are overlapping with the bound of any other marker (or cluster). If overlapping with another marker, combine the two in a new cluster. If overlapping with a cluster, add the marker to the cluster.
Every zoom level has it's own set of clusters. This is where things get computationally a little bit bigger. There are about 18 levels for regular maps. So you have to recompute the clusters on every level, as they are normally different for every level. (My script does this, of course, as it's the only way.)
To do this we obviously need to know the bounds of the image. And that's a problem in v3. It clearly needs to compute these bounds. In fact, it has
these bounds. But it doesn't expose them... :(
To create a marker you have to ways of specifying its icon. Either give a string that points to the image to be used as is, or supply a MarkerImage object that tells gmaps which image to use. Optionally you can use it to create markers using a classic sprite map, change the position of the anchor or to resize the original image to certain dimensions. Using the object is our problem here, as the object itself has virtually no api. And the api that exists is broken. Meh.
So according to the api
the object should expose a
property. However, it doesn't expose the
so I can't use it. Otherwise I could've worked around the rest. Pity.
When you check
it will return whatever you used to create the icon. For the object you will see five properties (same as the five arguments of the constructor), but only one is not changed by the minifier;
. So we get
, albeit undocumented (==bad). We can get the url (it's the only string type property in the enumeration), also undocumented. And we can get the
because it's the only other
, but it's useless on it's own.
There exists another marker property though. It's called
. It is also undocumented, but it is exactly what we want! Together with the origin of course. The pixel bounds are the bottom-left and top-right corner of the marker image, relative from the anchor. If only this was exposed... Alas, no such luck.
So as it stands, I decided to go the easy and safe way. The cluster manager right now demands the size and, if you changed it, the anchor to be supplied when you add markers. There's no documented alternative, I checked
So the cluster manager works okay. You can supply click events (setting the
property on the marker before adding it to the manager) which are accessible by clicking the marker and the info window of the cluster. You can also supply a dragend event (through the
property of the marker), which will fire after the manager re-processed the marker in the clusters. Note that after dragging, the marker is removed from and added to the map to fix attached events and cleanup.
You can also change the events using the
method of the manager. Easily extended, although I'm not happy with this mechanism. It's legacy.
As I said it's a conversion from an old script. It's a little bloated and could use a rewrite to something cleaner. But it works, and is relatively easy to use.Marker Cluster Manager for Google Maps v3
Hope it useful for you :)