My introductory question is:
What is the best way to bind data to a D3.js map?
I came up with three possibilities:
1. Apply a class based on the data, then apply style to the class.
.attr("class", function(d) { return quantize(rateById.get(d.id)); })
This option seems to be the most popular (see the choropleth block), but it is not actually binding the data. Thus it seems a bit dishonest to D3's intent. It also means the bound data cannot be simply referenced for tooltips, for example.
2. Merge the topo.json feature collection data with the rest of the data.
We can place all data inside a single variable (perhaps using forEach
). The result might look something like:
var mergeData = [{
area: "NW",
mean: 45,
features: {...}
}, {
area: "NE",
mean: 23,
features: {...}
}, {
area: "SW",
mean: 87,
features: {...}
}, {
area: "SE",
mean: 56,
features: {...}
}];
Then we can bind the mergeData
to path elements, generate the map by pointing the "d" attribute to d.features
, and style using d.mean
. This allows for each path element to contain all necessary data we will ever need. However, it seems cumbersome to have to combine it all together.
3. Replace the feature collection data with the desired data.
Feature collection data should not be needed once the map shapes have been drawn. It can be replaced. Why not simply bind the data we need to these paths (after generating the map shapes) so it is always available for styling, tooltips, etc.? We just need a key function to match the new data to the correct map shape. The update selection will take care of the replacement.
d3.selectAll(".area").data(data, function(d) {
return d.id;
})
.style("fill", function(d) {
return colorScale(d.mean);
});
This seems to work well and is nice and clean. The caveat is the structure of the data and the feature collection must be the same. The same key function is evaluated on both sets of data. So if our data is nested, we cannot access d.values.id
because there is no corresponding values
in the feature collection - it is simply d.id
. Intuitively, I'm wishing for a key function working like something akin to filter
, so that we could specify the location of the id
in each data set, something like so:
return d.id === d.values.id;
There is no way to accomplish this, is there?
A workaround would be to refactor the nested data into a third, flattened data variable so it is no longer nested. That way d.id
could be used in the key function. But once again this seems cumbersome.
Which is the best way? Or is there another?
I'm still learning D3, so please feel free to correct any of my assumptions or edit my question directly.