A few weeks ago, a club I’m a member of was updating its membership information. Since the data was being collected in a Google spread sheet, I thought it would be interesting to create a map visualization to show where the members come from. In this post, we’ll write a map overlay which will generate a display like the one in the following image using data drawn from a Google spread sheet. Basically, we’re going to give a map a nasty rash.
A note on Access Control
Since the data resides in a Google document, the user’s browser would need to be able to access that document for an overlay to be displayed. For the purposes of this document, our dummy data has been made publicly viewable. In your case of course, you may not want to expose your data to the world, so you should just share out the data to the people who need it. They will be able to view the overlay if they’re already signed into a Google account you’ve given access to.
We’ll also be using the Visualization API (version 1) to deal with our data source. This is loaded in the same manner as any other API hosted by Google:
After which we can ask it to load any spread sheet we need.
Although I’m using the spread sheet URL here, there’s no reason to load the whole thing – I’m only doing that here because there isn’t anything else in the spread sheet. The query object itself has a setQuery method which can take expressions as needed.
The query’s send method takes a callback, which is where we tell it to display our map and create the overlay with all the bubbles.
Creating the Overlay
A custom overlay is created by extending the OverlayView class and defining its drawing behaviour. Since we’ll be using map markers, we won’t need to constantly keep them updated when the view is zoomed and panned – the API already handles that. We can just create the markers in the onAdd function, and we’re set:
The drawBubble function then creates a marker of the appropriate size and sticks it to the map:
Since the radius of the marker is expressed in an actual real world size (it’s a variable number of metres multiplied by the percentage size of the locality; so if the variable is 10 and the locality represents 20% of the population, then the bubble will be 200m in radius), it will scale with the map.
The problem with text
Unlike markers, text is not supported directly by the API, which means we’re SOL if we want to add labels quickly. Luckily, we can still add labels as html elements, and pin them on to the map using absolute positioning – the fromLatLngToDivPixel allows us to change a Latitude and Longitude value into a pixel coordinate on our map display, so we can put text up to our hearts’ content:
Note the check for null values before creating a new text container. Since the text elements are not fire and forget, we have to look after them ourselves, as they will not scale; since they’re positioned in relation to a pixel coordinate, they’ll also stay put regardless of how much we pan the map. To have the label maintain some kind of meaningful connection to whatever it’s labelling, we need to keep this up to date with the movements of our map – something which we can achieve with the draw function, which is called after onAdd, and any time the map is scaled or moved:
To add the bubbles to a map, first get the data source, then create an instance of the overlay and the map. Finally, attach it to the map:
Defining the data in your script
If you don’t want to use a Google spread sheet as a data source, and you can always define the data yourself. You can set up the data as follows:
Once the rows have been added, you can use the data object in the same way as one you would have received in response to query.send.
The constructor for the bubble overlay takes an optional parameter which controls the way the display is built up. The defaults are as follows:
radiusForPercentage defines how large each bubble, and is given in metres. Each 1% of the total population (see above) will increase the bubble radius by this amount.
text.visible determines whether text will be displayed, or not.
text.minimumZoom tells you how close you have to zoom in for text labels to be displayed. This prevents smaller maps (such as the one I’m using in this example) from becoming unreadable clutters of alphabet soup gone wrong.
The bubble parameters should be fairly self explanatory.
As usual, any suggestions would be welcome, though there’s no guarantee I’ll actually do anything about them.