Unlike choropleth maps, cartograms encode data using area rather than color, resulting in distorted geographic boundaries. In this example, states are shown as circles centered arond their geographic position. The size of the circle is based on teh 2015 GDP as reported on Wikipedia.
This is forked from mbostock's block: Non-Contiguous Cartogram. The original was inspired by Zachary Johnson. Non-continguous cartogram design invented by Judy Olsen. U.S. state and county boundaries from the U.S. Census Bureau, simplified using GDAL and MapShaper.
xxxxxxxxxx
<meta charset="utf-8">
<title>Non-Contiguous Cartogram</title>
<style>
.land {
fill: #fff;
stroke: #ccc;
}
.state {
stroke: #666;
opacity: .5;
}
</style>
<body>
<script src="//d3js.org/d3.v3.min.js"></script>
<script src="//d3js.org/topojson.v1.min.js"></script>
<script>
var path = d3.geo.path();
var svg = d3.select("body").append("svg")
.attr("width", 1200)
.attr("height", 500);
// set up pie chart
var radius = 130;
var arc = d3.svg.arc()
.outerRadius(radius - 10)
.innerRadius(0);
var sortSlices = function(a,b){
if(a.include == b.include) {
return b.GDP - a.GDP
}
return +b.include - +a.include
}
var updatePie = function(data){
var pie = d3.layout.pie()
.sort(null)
.value(function(d) { return +d.GDP; });
svg.selectAll(".arc").data([]).exit().remove()
var g = svg.selectAll(".arc")
.data(pie(data.sort(sortSlices)), function(d){return d.data.State})
console.log(pie(data.sort(sortSlices)))
g.enter().append("g")
.attr("transform", "translate(950,300)")
.attr("class", "arc")
.append("path")
.attr("class", 'stateArc');
d3.selectAll(".stateArc").attr("d", arc).style("fill", colorSelectedArc);
}
d3.csv('namesToId.csv', function(error, stateNamesIds){
var stateAbbrLookup = {}
var stateIdLookup = stateNamesIds.reduce(function(map, obj) {
map[obj.id] = obj.name
stateAbbrLookup[obj.id] = obj.abb
return map;
}, {});
d3.csv('stateGDP2015.csv', function(error, gdp){
var max = d3.max(gdp, function(d) { return +d.GDP;} );
var gdpByStateLookup = gdp.reduce(function(map, obj) {
if(obj.GDP > max){
max = obj.GDP
}
map[obj.State] = {'gdp': +obj.GDP, 'include': +obj.include};
return map;
}, {});
d3.json("https://gist.githubusercontent.com/mbostock/4090846/raw/d534aba169207548a8a3d670c9c2cc719ff05c47/us.json", function(error, us) {
if (error) throw error;
// outline of US
svg.append("path")
.datum(topojson.feature(us, us.objects.land))
.attr("class", "land")
.attr("d", path);
// function to use include boolean to decide color
colorSelected = function(d){
var data = gdpByStateLookup[stateIdLookup[d.id]];
if(data && data.include) {
return "#9fc2c6"
}
return "#ccc"
}
colorSelectedArc = function(d) {
return +d.data.include ? '#9fc2c6' : '#ddd'
}
var state = svg.selectAll(".state")
.data(topojson.feature(us, us.objects.states).features, function(d){return d.id})
.enter().append("g")
.attr("transform", function(d){
var center = path.centroid(d);
return "translate(" + center[0] + "," + center[1] + ")"
})
state.append("text")
.attr("transform", function(d){return "translate(-11,5)"})
.attr("fill", "lightgrey")
.text(
function(d){
return stateAbbrLookup[d.id]
}
)
state.append("circle")
.attr("class", "state")
.attr("r", function(d){
var data = gdpByStateLookup[stateIdLookup[d.id]];
return data ? Math.sqrt(data.gdp / max) * 50 : 0
})
.style("fill", colorSelected)
.on('click', function(d){
gdpByStateLookup[stateIdLookup[d.id]].include = !gdpByStateLookup[stateIdLookup[d.id]].include;
gdp.forEach(function(state){
if(state.State == stateIdLookup[d.id]){
state.include = !+state.include}
})
d3.selectAll(".state")
.style("fill", colorSelected);
updatePie(gdp)
})
updatePie(gdp)
});
});
})
</script>
https://d3js.org/d3.v3.min.js
https://d3js.org/topojson.v1.min.js