javascriptjsond3.jstopojson

Datum-Data difference in map behavior in d3


I'm pretty new to d3js and trying to understand the difference between using data and datum to attach data to elements. I've done a fair bit of reading the material online and I think I theoretically understand what's going on but I still lack an intuitive understanding. Specifically, I have a case where I'm creating a map using topojson. I'm using d3js v7.

In the first instance, I have the following code to create the map within a div (assume height, width, projection etc. setup correctly):

var svg = d3.select("div#map").append("svg")
    .attr("width", width)
    .attr("height", height)
    .attr("transform", "translate(" + 15 + "," + 0 + ")"); 

var path = d3.geoPath()
          .projection(projection);

var mapGroup = svg.append("g");

d3.json("json/world-110m.json").then(function(world){
  console.log(topojson.feature(world, world.objects.land))

  mapGroup.append("path") 
     .datum(topojson.feature(world, world.objects.land))
     .attr("class", "land") 
     .attr("d", path); 

});

The console log for the topojson feature looks like this: enter image description here

And the map comes out fine (with styling specified in a css file):enter image description here

But if I change datum to data, the map disappears. I'm trying to improve my understanding of how this is working and I'm struggling a little bit after having read what I can find online. Can someone explain the difference between data and datum as used in this case and why one works and the other doesn't?

Thanks for your help!


Solution

  • There are several differences between data() and datum(), but for the scope of your question the main difference is that data() accepts only 3 things:

    As you can see, topojson.feature(world, world.objects.land) is an object. Thus, all you'd need to use data() here (again, not the idiomatic D3, I'm just addressing your specific question) is wrapping it with an array:

    .data([topojson.feature(world, world.objects.land)])
    

    Here is your code using data():

    var svg = d3.select("div#map").append("svg")
      .attr("width", 500)
      .attr("height", 300)
      .attr("transform", "translate(" + 15 + "," + 0 + ")");
    
    var path = d3.geoPath();
    
    var mapGroup = svg.append("g");
    
    d3.json("https://raw.githubusercontent.com/d3/d3.github.com/master/world-110m.v1.json").then(function(world) {
    
      const projection = d3.geoEqualEarth()
        .fitExtent([
          [0, 0],
          [500, 300]
        ], topojson.feature(world, world.objects.land));
    
      path.projection(projection);
    
      mapGroup.append("path")
        .data([topojson.feature(world, world.objects.land)])
        .attr("class", "land")
        .attr("d", path);
    
    });
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
    <script src="https://unpkg.com/topojson@3"></script>
    <div id="map"></div>