Search code examples
javascriptd3.jstopojson

d3js v5 + Topojson v3 Map not rendering


I don't know why, according to the topojson version (may be) I have:

TypeError: t is undefined

An explanation could be nice! (I use the last version of topojson.)

Here an example of TypeError is undefined (pointing to topojson file)

<!DOCTYPE html>
<html>

  <head>
    <link rel="stylesheet" href="style.css">
    <script src="https://unpkg.com/[email protected]/dist/d3.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>
  </head>

  <body>


  <svg width="960" height="600"></svg>

  <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();




    var files = ["https://d3js.org/us-10m.v1.json", "unemployment.tsv"];
    var promises1 = d3.json("https://d3js.org/us-10m.v1.json");
    var promises2 = d3.tsv("unemployment.tsv");



    Promise.all([promises1, promises2]).then(function(us){
        console.log(us[0]);
        console.log(us[1]);


      svg.append("g")
          .attr("class", "counties")
        .selectAll("path")
        .data(topojson.feature(us, us[0].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[0].objects.states, function(a, b) { return a !== b; }))
          .attr("class", "states")
          .attr("d", path);
      });

  </script>



  </body>

</html>

My code : https://plnkr.co/edit/EzcZMSEQVzCt4uoYCLIc?p=info

Original (d3js v4 + Topojson v2) : https://bl.ocks.org/mbostock/4060606

Here an another example of TypeError is undefined (pointing to topojson file)

My code : https://plnkr.co/edit/o1wQX3tvIDVxEbDtdVZP?p=preview


Solution

  • The two examples have two separate issues in relation to topojson.

    In the first example you update where the topojson is held from us to us[0] due to the change in how files are fetched. However, you haven't quite updated the code to reflect this change:

    In Original: .data(topojson.feature(us, us.objects.counties).features)

    In Question: .data(topojson.feature(us, us[0].objects.counties).features)

    And fixed: .data(topojson.feature(us[0], us[0].objects.counties).features)

    Updated plunkr.


    However, the issue in the second example is a little different.

    topojson.feature requires two parameters, a topology and an object. The topology is the variable holding the json, which you have correct. However, the object is not arcs. The variable holding the topojson has a property called objects, and in that there will always be at least one property representing a feature collection (states, counties, etc). This object (or one of these objects) is what we want.

    Here is a snippet of your topojson:

    ... "objects":{"dep_GEN_WGS84_UTF8":{"type":"GeometryCollection","geometries":[{"arcs ..."

    We want topojson.feature(data,data.objects.dep_GEN_WGS84_UTF8).

    If making topojson with tools such as mapshaper, the object we want to display is the same as the name of the file used to create it. Generally, a quick word search through the topojson for "object" will also get you to the proper object pretty quick.

    The arcs property in a topojson is convenient storage for the pieces that make up the features, not the features themselves.

    Updated plunkr.


    In both cases the topology parameter passed to topojson.feature won't contain the specified features, generating the same error.