You are attaching 9420 event listeners (three for each county). A better solution would be to do the equivalent of delegate in jQuery:
var counties = map.append('g');
counties.attr('class', 'counties')
.selectAll('path')
.data(countylines.features)
.enter().append('path')
.attr('d', path);
counties.on('mouseover', function(){
if (d3.event.target.tagName == "path")
showCaption();
})
.on('mouseout', function() {
if (d3.event.target.tagName != "path")
caption.html(starter);
});
You also don't need to repeat the process on mousemove, that triggers dozens of events per second for what mouseover and mouseout already accomplished.
I recently built a web-based tool for visualizing usage of Tel Aviv's municipal bicycle rental system using D3 and mapbox (warning, hebrew): http://telostats.com
Source is up at https://github.com/idan/telostats, janky though it may be. This was my first effort with geo and D3, and I'm a python person, so the JS is probably eight different kinds of terrible, but hopefully it's useful. There are a lot of examples like the OP's blogpost where D3 is used to render geometry, but very few that show you how to use D3 as an informational layer above an existing slippymap (which is what we did).
I think the value of TopoJSON is a bit understated here. MBostock reported some pretty awesome results about a month ago. "With TopoJSON, reduced 2.2M shapefile to 628K (-72%, 176K gzipped)!" https://twitter.com/mbostock/status/269548330663677952
Additionally, smoothing shapefiles with tools like http://mapshaper.org can really help trim down filesizes.
Absolutely agree. I'm currently in the process of producing a thematic map as part of a set of data visualisations[1]. TopoJSON is proving to be an absolute godsend. It's allowed us to reduce ~100MB+ geoJSON down to ~1MB and the shared arc's means we can include multiple levels of boundaries without duplicating the data needed to be sent over the network.
There's still some issues we're having. SVG rendering performance isn't fast or consistent across browsers for 3000+ polygons and although TopoJSON shares arcs between common boundaries the rendered polygons don't. Also the large differences in the size of boundaries we're dealing with means the map needs to be zoom-able. Delivering higher detail boundaries dynamically is tough problem to solve. Ideally you could request a bounding box from a TopoJSON based server, and it would deliver a delta of higher detailed points precomputed from Visvalingam’s algorithm[2].
I found the default quantization level to be much too blocky. For anyone working with TopoJSON I'd recommend you first raise the quantization level while looking at your smallest features until they're no longer blocky, then use the inbuilt simplification to bring the file size down to manageable.
Hey, that's fantastic. Thank you for the feedback. I can see how you would need to raise TopoJSON's quantization level for creating zoomable geometry; the default level is designed for display approximately 10,000px by 10,000px, and you'd quickly exceed that if you allow people to zoom in. I probably wouldn't want to change the default, though, as that would make the files larger than necessary for static display. (Perhaps there's a way to decide the appropriate quantization automatically?)
A useful technique with zoomable geometry is using Visvalingam's algorithm to precompute the salience (visual importance) of each point, but not prefiltering; then, you filter the points based on the zoom level during rendering. Thus, the rendered detail increases as you zoom in, but you only need to download one TopoJSON file.
Depending on how much zooming you're doing, breaking the geometry into tiles and zoom-level specific data would be helpful (à la Polymaps), but I think for many visualization applications it's overkill. For example, a single TopoJSON file of U.S. states and counties with enough detail for both a national and state-specific view is easily doable without tiling.
The next release of D3 (3.0) includes streaming geometry transformations. This can enable fast filtering of geometry using the precomputed salience during rendering as described above without the overhead of creating multiple copies of the geometry during the rendering pipeline. Also, D3 3.0 supports rendering directly to Canvas, which is excellent for animated or interactive changes to the projection. For example:
TopoJSON's --simplify flag uses Visvalingam's algorithm. And, since it's the mesh that is simplified rather than independent features, shared borders between features will be preserved. Without this feature, you end up with something that resembles shattered glass:
Concerning the size of the json file with geo path, I don't know if you can rename the attributes, but you can save 300ko just by using simplified coordinates ("-86.411786" becomes "-86.4"). I don't think you need sub-pixel precision :)
Try it out, I'm sure it will feel snappier.