2
\$\begingroup\$

I've taught myself d3 (still learning), and have a finished visualisation that I'm more than happy with.

But i think (know) that the code can be improved and made more efficient. For instance, use a csv file for the data (don't know where to begin with that).

I believe i'm pretty good with html and css, but i was a little daunted by d3 and the html/css are a little messy (see the <p></p> in both the html css files).

I've attached an image of what it looks like (please ignore the data under South England: I know it's duplicated; I have to replace them with the actual data).

@import url("https://fonts.googleapis.com/css?family=Lato:300");

rect {
  stroke: white;
}
text {
  font-family: "Lato";
  fill: white;
  font-size: 8px;
  text-anchor: middle;
}

svg {
  padding-left: 60px;
  padding-top:17px;
  position: fixed;
}

p {
  font-size: 14px;
}

.p1 {
  position: absolute;
  font-family: "Lato";
  font-size: 17px;
  padding-left: 60px;
}

.p2 {
  position: absolute;
  font-family: "Lato";
  font-size: 17px;
  padding-left: 550px;
}

.p3 {
  position: absolute;
  font-family: "Lato";
  padding-left: 60px;
  padding-top: 35px;
}

.p4 {
  position: absolute;
  font-family: "Lato";
  padding-left: 230px;
  padding-top: 35px;
}

.p5 {
  position: absolute;
  font-family: "Lato";
  padding-left: 380px;
  padding-top: 35px;
}

.p6 {
  position: absolute;
  font-family: "Lato";
  padding-left: 60px;
  padding-top: 390px;
}

.p7 {
  position: absolute;
  font-family: "Lato";
  padding-left: 283px;
  padding-top: 390px;
}

.p8 {
  position: absolute;
  font-family: "Lato";
  padding-left: 553px;
  padding-top: 35px;
}

.p9 {
  position: absolute;
  font-family: "Lato";
  padding-left: 723px;
  padding-top: 35px;
}

.p10 {
  position: absolute;
  font-family: "Lato";
  padding-left: 896px;
  padding-top: 35px;
}

.p11 {
  position: absolute;
  font-family: "Lato";
  padding-left: 553px;
  padding-top: 380px;
}

.p12 {
  position: absolute;
  font-family: "Lato";
  padding-left: 900px;
  padding-top: 380px;
}
<!DOCTYPE html>
<meta charset="utf-8">
<head>
  <title>TreeMap</title>
  <link rel="stylesheet" type="text/css" href="stylesheet.css" />
</head>

<body>
  <svg width="1200" height="720">
    <g></g>
    <section class="container1">
      <p class="p1"> North England </p><p class="p2"> South England </p>
      <p class="p3"> Bradford City </p><p class="p4"> Leeds </p>
      <p class="p5"> Liverpool </p><p class="p6"> Manchester </p>
      <p class="p7"> Sheffield </p><p class="p8"> Brighton </p>
      <p class="p9"> Bristol </p><p class="p10"> Luton </p>
      <p class="p11"> Milton Keynes </p><p class="p12"> Southampton </p>
      <object class="Box" class="cancer"></object>
    </section>
  </svg>

  <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.2.2/d3.min.js"></script>
  <script>
var data = {
  "name": "England",
  "children": [
    {
      "name": "North England",
      "children": [
        {
          "name": "Bradford City",
          "children": [
            {
              "name": "Cancer",
              "value": 1.18,
              "color": "#A8A7A7",
            },
            {
              "name": "CKD",
              "value": 3.21,
              "color": "#2F9599",
            },
            {
              "name": "CHD",
              "value": 2.57,
              "color": "#E8175D",
            },
            {
              "name": "Diabetes",
              "value": 11.92,
              "color": "#474747",
            },
            {
              "name": "Stroke",
              "value": 1.18,
              "color": "#CC527A",
            }
          ]
        },
        {
        "name": "Leeds",
        "children": [
          {
            "name": "Cancer",
            "value": 2.50,
            "color": "#A8A7A7",
          },
          {
            "name": "CKD",
            "value": 3.52,
            "color": "#2F9599",
          },
          {
            "name": "CHD",
            "value": 3.29,
            "color": "#E8175D",
          },
          {
            "name": "Diabetes",
            "value": 6.71,
            "color": "#474747",
          },
          {
            "name": "Stroke",
            "value": 1.82,
            "color": "#CC527A",
          }
        ]
      },
      {
        "name": "Liverpool",
        "children": [
          {
            "name": "Cancer",
            "value": 2.50,
            "color": "#A8A7A7",
          },
          {
            "name": "CKD",
            "value": 5.90,
            "color": "#2F9599",
          },
          {
            "name": "CHD",
            "value": 3.71,
            "color": "#E8175D",
          },
          {
            "name": "Diabetes",
            "value": 6.70,
            "color": "#474747",
          },
          {
            "name": "Stroke",
            "value": 1.88,
            "color": "#CC527A",
          }
        ]
      },
      {
        "name": "Manchester",
        "children": [
          {
            "name": "Cancer",
            "value": 1.78,
            "color": "#A8A7A7",
          },
          {
            "name": "CKD",
            "value": 2.95,
            "color": "#2F9599",
          },
          {
            "name": "CHD",
            "value": 2.61,
            "color": "#E8175D",
          },
          {
            "name": "Diabetes",
            "value": 7.05,
            "color": "#474747",
          },
          {
            "name": "Stroke",
            "value": 2.13,
            "color": "#CC527A",
          }
        ]
      },
      {
        "name": "Sheffield",
        "children": [
          {
            "name": "Cancer",
            "value": 2.54,
            "color": "#A8A7A7",
          },
          {
            "name": "CKD",
            "value": 4.57,
            "color": "#2F9599",
          },
          {
            "name": "CHD",
            "value": 3.85,
            "color": "#E8175D",
          },
          {
            "name": "Diabetes",
            "value": 7.08,
            "color": "#474747",
          },
          {
            "name": "Stroke",
            "value": 2.13,
            "color": "#CC527A",
          }
        ]
      },
    ]
  },
  {
    "name": "South England",
    "children": [
      {
        "name": "Brighton",
        "children": [
          {
            "name": "Cancer",
            "value": 1.18,
            "color": "#A8A7A7",
          },
          {
            "name": "CKD",
            "value": 3.21,
            "color": "#2F9599",
          },
          {
            "name": "CHD",
            "value": 2.57,
            "color": "#E8175D",
          },
          {
            "name": "Diabetes",
            "value": 11.92,
            "color": "#474747",
          },
          {
            "name": "Stroke",
            "value": 1.18,
            "color": "#CC527A",
          }
        ]
      },
      {
        "name": "Bristol",
        "children": [
          {
            "name": "Cancer",
            "value": 1.18,
            "color": "#A8A7A7",
          },
          {
            "name": "CKD",
            "value": 3.21,
            "color": "#2F9599",
          },
          {
            "name": "CHD",
            "value": 2.57,
            "color": "#E8175D",
          },
          {
            "name": "Diabetes",
            "value": 11.92,
            "color": "#474747",
          },
          {
            "name": "Stroke",
            "value": 1.18,
            "color": "#CC527A",
          }
        ]
      },
      {
        "name": "Luton",
        "children": [
          {
            "name": "Cancer",
            "value": 1.18,
            "color": "#A8A7A7",
          },
          {
            "name": "CKD",
            "value": 3.21,
            "color": "#2F9599",
          },
          {
            "name": "CHD",
            "value": 2.57,
            "color": "#E8175D",
          },
          {
            "name": "Diabetes",
            "value": 11.92,
            "color": "#474747",
          },
          {
            "name": "Stroke",
            "value": 1.18,
            "color": "#CC527A",
          }
        ]
      },
      {
        "name": "Milton Keynes",
        "children": [
          {
            "name": "Cancer",
            "value": 1.18,
            "color": "#A8A7A7",
          },
          {
            "name": "CKD",
            "value": 3.21,
            "color":"#2F9599",
          },
          {
            "name": "CHD",
            "value": 2.57,
            "color": "#E8175D",
          },
          {
            "name": "Diabetes",
            "value": 11.92,
            "color": "#474747",
          },
          {
            "name": "Stroke",
            "value": 1.18,
            "color": "#CC527A",
          }
        ]
      },
      {
        "name": "Southampton",
        "children": [
          {
            "name": "Cancer",
            "value": 1.18,
            "color": "#A8A7A7",
          },
          {
            "name": "CKD",
            "value": 3.21,
            "color": "#2F9599",
          },
          {
            "name": "CHD",
            "value": 2.57,
            "color": "#E8175D",
          },
          {
            "name": "Diabetes",
            "value": 11.92,
            "color": "#474747",
          },
          {
            "name": "Stroke",
            "value": 1.18,
            "color": "#CC527A",
          }
        ]
      },
      ]
    }
  ]
};

var treemapLayout = d3.treemap()
  .size([1000, 600])
  .paddingTop(20)
  .paddingInner(8);

var rootNode = d3.hierarchy(data)

rootNode.sum(function(d) {
  return d.value;
});

treemapLayout(rootNode);

var nodes = d3.select('svg g')
  .selectAll('g')
  .data(rootNode.descendants())
  .enter()
  .append('g')
  .attr('transform', function(d) {return 'translate(' + [d.x0, d.y0] + ')'})

  nodes
    .append('rect')
    .attr('width', function(d) { return d.x1 - d.x0; })
    .attr('height', function(d) { return d.y1 - d.y0; })
    .attr('opacity', function(d) { return 0.8 /* has to be like d.value / max or so */ })
    .attr('fill', function(d) { if (d.data.color) {
                                  return d.data.color
                                } else {
                                  return "white"
                                }})
  nodes
    .append('text')
    .attr('dx', 10)
    .attr('dy', 8)
		.text(function(d) {
      return d.data.value;
  });


  </script>
</body>

Screenshot of output

\$\endgroup\$
  • \$\begingroup\$ Welcome to Code Review! Please tell us more about what the data represent, how you generated the data structure, and what this data visualization aims to accomplish. See How to Ask. \$\endgroup\$ – 200_success Feb 15 '18 at 22:57
  • \$\begingroup\$ Its for a university project, its from our healthcare system the NHS. Basically, for some context, according to various articles, there is a divide between the north of England and south. Where the north of the country apparently has a lesser quality of life and ultimately, die younger (research done by the university of Manchester). With this data, i simply want to compare various illnesses and diseases between the 5 biggest cities in the north of England, and the 5 biggest cities in the south of England (biggest cities based on population size). Cheers! \$\endgroup\$ – fellinlovewithaman Feb 15 '18 at 23:23
  • \$\begingroup\$ The thing is, you've hard-coded a bunch of colours in data and position coordinates in the CSS, so this is sort of like asking us to review the PDF output rather than the process that generates the visualization. Kind of an awkward situation for reviewers. \$\endgroup\$ – 200_success Feb 15 '18 at 23:26
  • \$\begingroup\$ Is there some reason you opted for TreeMap layouts rather than stacked bar graphs, which would be easier to compare visually? \$\endgroup\$ – 200_success Feb 15 '18 at 23:33
  • 1
    \$\begingroup\$ But that's just chartjunk. You're probably causing Edward Tufte to have a stroke right now. \$\endgroup\$ – 200_success Feb 15 '18 at 23:42
0
\$\begingroup\$

As already very well explained in the comments section, the treemap is not the best visual encoding to represent your data. However, suggesting how to better represent the data you have is arguably off topic even for Code Review. With that in mind, I'll address only a couple of topics in your code regarding best practices, specially that ugly <p> positioning.

As I said, the creation of HTML <p> elements and their positioning in the CSS is not only strange and cumbersome, but it also will hider you in several ways. The common (and idiomatic) way to create the city names is using the very D3 layout you have, in the SVG itself.

First of all, don't use magic numbers. Set explicitly the width and height...

var width = 1000,
  height = 600;

... and use them to append your SVG:

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

Then, you can use those values in the layout:

var treemapLayout = d3.treemap()
  .size([width, height])
  .paddingTop(20)
  .paddingInner(8);

Now comes the important part, which is printing the city names. First, get rid of all that HTML and CSS regarding the <p> elements. Also, remove the position: fixed for the SVG.

Have in mind that your layout has all the x and y positions you need. So, to print the city names, which have the depth === 2, just do:

var cities = nodes.filter(function(d) {
    return d.depth === 2
  }).append("text")
  .attr("class", "city")
  .attr("dy", "1em")
  .text(function(d) {
    return d.data.name;
  });

Finaly, it's a good idea naming your selections. For instance:

var rects = nodes.append('rect')
  //etc...

If you want to print the regions' names, just use the same logic.

Here is your code with those changes:

var data = {
  "name": "England",
  "children": [{
      "name": "North England",
      "children": [{
          "name": "Bradford City",
          "children": [{
              "name": "Cancer",
              "value": 1.18,
              "color": "#A8A7A7",
            },
            {
              "name": "CKD",
              "value": 3.21,
              "color": "#2F9599",
            },
            {
              "name": "CHD",
              "value": 2.57,
              "color": "#E8175D",
            },
            {
              "name": "Diabetes",
              "value": 11.92,
              "color": "#474747",
            },
            {
              "name": "Stroke",
              "value": 1.18,
              "color": "#CC527A",
            }
          ]
        },
        {
          "name": "Leeds",
          "children": [{
              "name": "Cancer",
              "value": 2.50,
              "color": "#A8A7A7",
            },
            {
              "name": "CKD",
              "value": 3.52,
              "color": "#2F9599",
            },
            {
              "name": "CHD",
              "value": 3.29,
              "color": "#E8175D",
            },
            {
              "name": "Diabetes",
              "value": 6.71,
              "color": "#474747",
            },
            {
              "name": "Stroke",
              "value": 1.82,
              "color": "#CC527A",
            }
          ]
        },
        {
          "name": "Liverpool",
          "children": [{
              "name": "Cancer",
              "value": 2.50,
              "color": "#A8A7A7",
            },
            {
              "name": "CKD",
              "value": 5.90,
              "color": "#2F9599",
            },
            {
              "name": "CHD",
              "value": 3.71,
              "color": "#E8175D",
            },
            {
              "name": "Diabetes",
              "value": 6.70,
              "color": "#474747",
            },
            {
              "name": "Stroke",
              "value": 1.88,
              "color": "#CC527A",
            }
          ]
        },
        {
          "name": "Manchester",
          "children": [{
              "name": "Cancer",
              "value": 1.78,
              "color": "#A8A7A7",
            },
            {
              "name": "CKD",
              "value": 2.95,
              "color": "#2F9599",
            },
            {
              "name": "CHD",
              "value": 2.61,
              "color": "#E8175D",
            },
            {
              "name": "Diabetes",
              "value": 7.05,
              "color": "#474747",
            },
            {
              "name": "Stroke",
              "value": 2.13,
              "color": "#CC527A",
            }
          ]
        },
        {
          "name": "Sheffield",
          "children": [{
              "name": "Cancer",
              "value": 2.54,
              "color": "#A8A7A7",
            },
            {
              "name": "CKD",
              "value": 4.57,
              "color": "#2F9599",
            },
            {
              "name": "CHD",
              "value": 3.85,
              "color": "#E8175D",
            },
            {
              "name": "Diabetes",
              "value": 7.08,
              "color": "#474747",
            },
            {
              "name": "Stroke",
              "value": 2.13,
              "color": "#CC527A",
            }
          ]
        },
      ]
    },
    {
      "name": "South England",
      "children": [{
          "name": "Brighton",
          "children": [{
              "name": "Cancer",
              "value": 1.18,
              "color": "#A8A7A7",
            },
            {
              "name": "CKD",
              "value": 3.21,
              "color": "#2F9599",
            },
            {
              "name": "CHD",
              "value": 2.57,
              "color": "#E8175D",
            },
            {
              "name": "Diabetes",
              "value": 11.92,
              "color": "#474747",
            },
            {
              "name": "Stroke",
              "value": 1.18,
              "color": "#CC527A",
            }
          ]
        },
        {
          "name": "Bristol",
          "children": [{
              "name": "Cancer",
              "value": 1.18,
              "color": "#A8A7A7",
            },
            {
              "name": "CKD",
              "value": 3.21,
              "color": "#2F9599",
            },
            {
              "name": "CHD",
              "value": 2.57,
              "color": "#E8175D",
            },
            {
              "name": "Diabetes",
              "value": 11.92,
              "color": "#474747",
            },
            {
              "name": "Stroke",
              "value": 1.18,
              "color": "#CC527A",
            }
          ]
        },
        {
          "name": "Luton",
          "children": [{
              "name": "Cancer",
              "value": 1.18,
              "color": "#A8A7A7",
            },
            {
              "name": "CKD",
              "value": 3.21,
              "color": "#2F9599",
            },
            {
              "name": "CHD",
              "value": 2.57,
              "color": "#E8175D",
            },
            {
              "name": "Diabetes",
              "value": 11.92,
              "color": "#474747",
            },
            {
              "name": "Stroke",
              "value": 1.18,
              "color": "#CC527A",
            }
          ]
        },
        {
          "name": "Milton Keynes",
          "children": [{
              "name": "Cancer",
              "value": 1.18,
              "color": "#A8A7A7",
            },
            {
              "name": "CKD",
              "value": 3.21,
              "color": "#2F9599",
            },
            {
              "name": "CHD",
              "value": 2.57,
              "color": "#E8175D",
            },
            {
              "name": "Diabetes",
              "value": 11.92,
              "color": "#474747",
            },
            {
              "name": "Stroke",
              "value": 1.18,
              "color": "#CC527A",
            }
          ]
        },
        {
          "name": "Southampton",
          "children": [{
              "name": "Cancer",
              "value": 1.18,
              "color": "#A8A7A7",
            },
            {
              "name": "CKD",
              "value": 3.21,
              "color": "#2F9599",
            },
            {
              "name": "CHD",
              "value": 2.57,
              "color": "#E8175D",
            },
            {
              "name": "Diabetes",
              "value": 11.92,
              "color": "#474747",
            },
            {
              "name": "Stroke",
              "value": 1.18,
              "color": "#CC527A",
            }
          ]
        },
      ]
    }
  ]
};

var width = 1000,
  height = 600;

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

var treemapLayout = d3.treemap()
  .size([width, height])
  .paddingTop(20)
  .paddingInner(8);

var rootNode = d3.hierarchy(data)

rootNode.sum(function(d) {
  return d.value;
});

treemapLayout(rootNode);

var nodes = svg.selectAll(null)
  .data(rootNode.descendants())
  .enter()
  .append('g')
  .attr('transform', function(d) {
    return 'translate(' + [d.x0, d.y0] + ')'
  });

var rects = nodes.append('rect')
  .attr('width', function(d) {
    return d.x1 - d.x0;
  })
  .attr('height', function(d) {
    return d.y1 - d.y0;
  })
  .attr('opacity', function(d) {
    return 0.8 /* has to be like d.value / max or so */
  })
  .attr('fill', function(d) {
    if (d.data.color) {
      return d.data.color
    } else {
      return "white"
    }
  })

var cities = nodes.filter(function(d) {
    return d.depth === 2
  }).append("text")
  .attr("class", "city")
  .attr("dy", "1em")
  .text(function(d) {
    return d.data.name;
  })

var labels = nodes.append('text')
  .attr("class", "label")
  .attr('dx', 10)
  .attr('dy', 8)
  .text(function(d) {
    return d.data.value;
  });
rect {
  stroke: white;
}

.city {
  fill: darkslategray;
  font-size: 12px;
}

.label {
  font-family: "Lato";
  fill: white;
  font-size: 8px;
  text-anchor: middle;
}

svg {
  padding-left: 60px;
  padding-top: 17px;
}

p {
  font-size: 14px;
}
<script src="https://d3js.org/d3.v4.min.js"></script>

\$\endgroup\$
  • \$\begingroup\$ Thank you for this. This is a learning experience for me, as i've never used javascript or any of it's packages before; i'm learning as i go. The hardest part (of D3) is that there are many ways to do things, like anything. I just need to find processes that i'm comfortable with and build on them. (On a side note, appending text is by far the most troublesome for myself). Finally, i'm well aware that a TreeMap isn't the best layout for this data, and that a bar chart of some description would be. But my lecturer specifically asked for more advanced layouts, whether they're appropriate or not. \$\endgroup\$ – fellinlovewithaman Feb 19 '18 at 16:03
  • \$\begingroup\$ Everyone works, and i understand everything. Only thing is that how do i target the regions of the data structure? \$\endgroup\$ – fellinlovewithaman Feb 19 '18 at 22:01
  • \$\begingroup\$ Just use the same logic I used here, choosing the adequate depth. \$\endgroup\$ – Gerardo Furtado Feb 19 '18 at 22:13
  • \$\begingroup\$ Perfect, i had no idea that you could do that. Thanks for all the help. One last thing, i don't entirely understand that 'var nodes = svg.selectAll(null) .data(rootNode.descendants())' part of the code. Why could i have not used what i had 'var nodes = d3.select('svg g') .selectAll('g')'? Is it bad practice? \$\endgroup\$ – fellinlovewithaman Feb 20 '18 at 11:05
  • \$\begingroup\$ @JabroniPepperoni I'm glad you asked this! No, it's not a bad practice at all, but it's unnecessary: I wrote a Q&A pair explaining what is selectAll(null), have a good reading: stackoverflow.com/q/46147231/5768908 \$\endgroup\$ – Gerardo Furtado Feb 20 '18 at 12:57

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.