mathewspete
mathewspete

Reputation: 38

D3.js Choropleth using data in JSON

I am trying to make a map similar to Mike Bostock's Choropleth (https://bl.ocks.org/mbostock/4060606).

<!DOCTYPE html>
<meta charset="utf-8">
<style>

.counties {
  fill: none;
}

.states {
  fill: none;
  stroke: #fff;
  stroke-linejoin: round;
}

</style>
<svg width="960" height="600"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script>
<script src="https://d3js.org/topojson.v2.min.js"></script>
<script>

var svg = d3.select("svg"),
    width = +svg.attr("width"),
    height = +svg.attr("height");

var unemployment = d3.map();

var path = d3.geoPath();

var x = d3.scaleLinear()
    .domain([1, 10])
    .rangeRound([600, 860]);

var color = d3.scaleThreshold()
    .domain(d3.range(2, 10))
    .range(d3.schemeBlues[9]);

var g = svg.append("g")
    .attr("class", "key")
    .attr("transform", "translate(0,40)");

g.selectAll("rect")
  .data(color.range().map(function(d) {
      d = color.invertExtent(d);
      if (d[0] == null) d[0] = x.domain()[0];
      if (d[1] == null) d[1] = x.domain()[1];
      return d;
    }))
  .enter().append("rect")
    .attr("height", 8)
    .attr("x", function(d) { return x(d[0]); })
    .attr("width", function(d) { return x(d[1]) - x(d[0]); })
    .attr("fill", function(d) { return color(d[0]); });

g.append("text")
    .attr("class", "caption")
    .attr("x", x.range()[0])
    .attr("y", -6)
    .attr("fill", "#000")
    .attr("text-anchor", "start")
    .attr("font-weight", "bold")
    .text("Unemployment rate");

g.call(d3.axisBottom(x)
    .tickSize(13)
    .tickFormat(function(x, i) { return i ? x : x + "%"; })
    .tickValues(color.domain()))
  .select(".domain")
    .remove();

d3.queue()
    .defer(d3.json, "https://d3js.org/us-10m.v1.json")
    .defer(d3.tsv, "unemployment.tsv", function(d) { 
      console.log(d);
      unemployment.set(d.id, +d.rate); })
    .await(ready);

function ready(error, us) {
  if (error) throw error;

  svg.append("g")
      .attr("class", "counties")
    .selectAll("path")
    .data(topojson.feature(us, us.objects.counties).features)
    .enter().append("path")
      .attr("fill", function(d) { return color(d.rate = unemployment.get(d.id)); })
      .attr("d", path)
    .append("title")
      .text(function(d) { return d.rate + "%"; });

  svg.append("path")
      .datum(topojson.mesh(us, us.objects.states, function(a, b) { return a !== b; }))
      .attr("class", "states")
      .attr("d", path);
}

</script>

Instead of using a TSV file, I would like to use JSON for my data. I've tried figuring out how to convert the script to use JSON, but haven't had success. Currently, I am using JSON for the example structured as:

[{"id": "01001", "rate": 5.1},
{"id": "01003", "rate": 4.9},
{"id": "01005", "rate": 8.6},
{"id": "01007", "rate": 6.2},
{"id": "01009", "rate": 5.1},
{"id": "01011", "rate": 7.1}]

Ultimately, I want to have a drop down to select what "column" to display and even figure out how filter a range of years. My final JSON structured like such:

[{
   "ID": "02",
   "Volume": "NULL",
   "SubCategory": "Other Housing",
   "Category": "Use",
   "Type": "PAB",
   "date": "2013-12-31",
   "State": "Ohio"
 },
 {
   "ID": "02",
   "Volume": 24800000,
   "SubCategory": "Student Loans",
   "Category": "Use",
   "Type": "PAB",
   "date": "2013-12-31",
   "State": "Ohio"
 },
 {
   "ID": "02",
   "DateExcelNo": "12/31/14",
   "Volume": 440200000,
   "SubCategory": "Mort Rev Bonds",
   "Category": "Use",
   "Type": "PAB",
   "date": "2014-12-31",
   "State": "Ohio"
 },
 {
   "ID": "02",
   "Volume": 1205000000,
   "SubCategory": "Total Cap",
   "Category": "VolumeCapacity",
   "Type": "PAB",
   "date": "2014-12-31",
   "State": "Ohio"
 },
 {   "ID": "01",
   "Volume": "NULL",
   "SubCategory": "Other Housing",
   "Category": "Use",
   "Type": "PAB",
   "date": "2013-12-31",
   "State": "Connecticut"
 },
 {
   "ID": "01",
   "Volume": 24800000,
   "SubCategory": "Student Loans",
   "Category": "Use",
   "Type": "PAB",
   "date": "2013-12-31",
   "State": "Connecticut"
 },
 {
   "ID": "01",
   "DateExcelNo": "12/31/14",
   "Volume": 440200000,
   "SubCategory": "Mort Rev Bonds",
   "Category": "Use",
   "Type": "PAB",
   "date": "2014-12-31",
   "State": "Connecticut"
 },
 {
   "ID": "01",
   "Volume": 1205000000,
   "SubCategory": "Total Cap",
   "Category": "VolumeCapacity",
   "Type": "PAB",
   "date": "2014-12-31",
   "State": "Connecticut"
 },
 {
   "ID": "01",
   "Volume": 663400000,
   "SubCategory": "Total Carryforward to Next Year",
   "Category": "Carryfwd",
   "Type": "PAB",
   "date": "2014-12-31",
   "State": "Connecticut"
 }]

Upvotes: 1

Views: 1555

Answers (1)

Gerardo Furtado
Gerardo Furtado

Reputation: 102174

The problem here is quite simple: d3.json does not accept a row function.

I believe that you suppose that, given that JSON you shared in your question, you could simply change this line...

.defer(d3.tsv, "unemployment.tsv", function(d) { unemployment.set(d.id, +d.rate); })

... to this:

.defer(d3.json, "unemployment.json", function(d) { unemployment.set(d.id, +d.rate); })

However, this will not work, because you cannot use that row function (everything after the second comma is the row function) with d3.json.

Solution:

Create your JSON file (here named "unemployment.json") and do this in the queue:

.defer(d3.json, "unemployment.json")

Then, in the ready function, load that JSON as a third parameter (here I'm calling it un):

function ready(error, us, un) {

Finally, populate your d3.map():

un.forEach(function(d) { 
    unemployment.set(d.id, +d.rate); 
});

This is the updated bl.ocks (only 6 counties have colour, of course, because I'm using your JSON with only 6 data points): https://bl.ocks.org/anonymous/211bf9775e61c5b07935536ed1926225

PS: it's a good practice asking just one problem per question at S.O. Thus, I suggest you post another question asking about the dropdown problem.

Upvotes: 4

Related Questions