ptitwolfy
ptitwolfy

Reputation: 25

Replacing D3.js CSV source with JSON

EDIT: as per below answer and for future reference, original source is World Choropleth by palewire

I am trying to reuse a bunch of Mike's examples by switching data source from CSV to JSON.

The original data source is loaded from a CSV file as follow:

// Load external data and boot
d3.queue()
    .defer(d3.json, "http://enjalot.github.io/wwsd/data/world/world-110m.geojson")
    .defer(d3.csv, "mooc-countries.csv", function(d) { data.set(d.code, +d.total); })
    .await(ready);

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

    // Draw the map
    svg.append("g")
        .attr("class", "countries")
        .selectAll("path")
        .data(topo.features)
        .enter().append("path")
            .attr("fill", function (d){
                // Pull data for this country
                d.total = data.get(d.id) || 0;
                // Set the color
                return colorScale(d.total);
            })
            .attr("d", path);
}

But I am trying the change the second .defer to get data from a JSON file, so far I figured out it should be close to this:

.defer(d3.json, "data.json", function(error, data) {
      data.forEach(function(d) {
          return {
               d.code,
               d.total;
             };
           });
         })

The JSON source:

[
  {
    "name" : "Burkina Faso",
    "total" : 5,
    "percent" : 0.3,
    "code" : "BFA"
  },
  {
    "name" : "Democratic Republic of the Congo",
    "total" : 4,
    "percent" : 0.3,
    "code" : "COD"
  },
  {
    "name" : "Haiti",
    "total" : 8,
    "percent" : 0.3,
    "code" : "HTI"
  }
]

Upvotes: 2

Views: 749

Answers (2)

altocumulus
altocumulus

Reputation: 21578

You did not include any link to the original Block but doing some googling I think you are referring to the World Choropleth py palewire. Porting this to use d3.json() requires some extra steps.

  1. There is a global variable data which is a d3.map:

    var data = d3.map();
    

    This dictionary holds the codetotal mappings extracted from the CSV and used later on when determining the fill. If you want to keep the general structure of the code you need to fill in the same values from the JSON input. This leads us directly towards the next issue.

  2. In the original code the map is populated by the function on this line:

    .defer(d3.csv, "mooc-countries.csv", function(d) { data.set(d.code, +d.total); })
    

    It is important, however, to understand that this function is not the callback handling the received dataset; that callback is hidden and is provided implicitly by the queue. Instead, this is the row conversion function which is executed per line of the CSV. This uses .set() to put each row into the map. Sticking to D3's naming convention that function's parameter is denoted d referring to a single datum instead of to the entire dataset.

    d3.json(), on the other hand, does not have a row conversion function because JSON data is inherently hierarchical, not row-based like a CSV. You can put that logic in the callback passed into .await():

    d3.queue()
      .defer(d3.json, "http://enjalot.github.io/wwsd/data/world/world-110m.geojson")
      .defer(d3.json, "data.json")   // No callback, just loading.
      .await(ready);
    
    function ready(error, topo, json) {
      if (error) throw error;
    
      var data = d3.map();   // Can be moved here, no need to pollute the global scope.
      json.forEach(function(d) { data.set(d.code, +d.total); });  // Populate the map.
      // ...business as usual
    }
    

There is still much room for improvement for the rest of the code (e.g. migrating to D3 v5 and ES6) but the above mentioned manipulations are probably the least intrusive way and will keep most of the original code untouched.

Upvotes: 1

calmar
calmar

Reputation: 1959

Your probably looking for return a new array with your selected keys. The return inside your forEach is the current problem. Perhaps you want to return this

maptransforms you array of objects into a different format, then you return to your defer function

.defer(d3.json, "data.json", function(error, data) {
      return data.map(function(d) {
          return {
               id: d.code,
               total: d.total;
             };
           });
         })

Upvotes: 0

Related Questions