javascripthtmld3.jschartsarea-chart

D3 area chart not rendering right


I'm creating a D3 area chart, and the area is showing above the line rather than below.

Area chart with area appearing above line

Here's a fiddle: https://jsfiddle.net/8xsrmgzw/

//Read the data
d3.csv("https://raw.githubusercontent.com/kalidogg/UCB/main/DebtPenny6.csv",

  // When reading the csv, I must format variables:
  function(d){
    return { date : d3.timeParse("%Y-%m-%d")(d.date), value : d.value }
  },

  // Now I can use this dataset:
  function(data) {

    // Add X axis --> it is a date format
    var x = d3.scaleTime()
      .domain(d3.extent(data, function(d) { return d.date; }))
      .range([ 0, width ]);
    svg.append("g")
      .attr("transform", "translate(0," + height + ")")
      .call(d3.axisBottom(x));

    // Add Y axis
    var y = d3.scaleLinear()
      .domain([0, d3.max(data, function(d) { return +d.value; })])
      .range([ height, 0 ]);
    svg.append("g")
      .call(d3.axisLeft(y));

    // Add the area
    svg.append("path")
      .datum(data)
      .attr("fill", "#cce5df")
      .attr("stroke", "#69b3a2")
      .attr("stroke-width", 1.5)
      .attr("d", d3.area()
        .x(function(d) { return x(d.date) })
        .y0(y(0))
        .y1(function(d) { return y(d.value) })
        )

})

</script>

Thanks.


Solution

  • There are a couple issues. First, you need to sort your data by the date before drawing the area. Second, your dataset contains null values. In the example below, I have filtered those out, but you could consider doing something like these examples to indicate where data is missing.

    Also, setting a stroke color on the area will create an outline of the area, including on the sides and bottom. If you just want a line outlining the top of the area, then you can add a separate path and use area.lineY1() to get a line generator for the top line of the area.

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="UTF-8">
        <script src="https://d3js.org/d3.v7.js"></script>
    </head>
    
    <body>
        <div id="chart"></div>
    
        <script>
    
          d3.csv("https://raw.githubusercontent.com/kalidogg/UCB/main/DebtPenny6.csv", function(d) {
            return { date : d3.timeParse("%Y-%m-%d")(d.date), value : +d.value }
          }).then(chart);
    
          function chart(data) {
            // set up
    
            const margin = { top: 10, bottom: 30, left: 50, right: 10};
    
            const width = 600 - margin.left - margin.right;
            const height = 200 - margin.top - margin.bottom;
    
            const svg = d3.select('#chart')
              .append('svg')
                .attr('width', width + margin.left + margin.right)
                .attr('height', height + margin.top + margin.bottom);
    
            const g = svg.append('g')
                .attr('transform', `translate(${margin.left},${margin.top})`);
    
            // filter and sort data
    
            data = data
                .filter(d => !isNaN(d.value))
                .sort((a, b) => d3.ascending(a.date, b.date));
    
            // scales
    
            const x = d3.scaleTime()
                .domain(d3.extent(data, d => d.date))
                .range([0, width]);
            
            const y = d3.scaleLinear()
                .domain([0, d3.max(data, d => d.value)])
                .range([height, 0]);
    
            // area generator
    
            const area = d3.area()
                .x(d => x(d.date))
                .y0(y(0))
                .y1(d => y(d.value));
    
            // Add the area and top line
    
            g.append("path")
                .datum(data)
                .attr("fill", "#cce5df")
                .attr("d", area);
           
            g.append("path")
                .datum(data)
                .attr("stroke", "#69b3a2")
                .attr("stroke-width", 1.5)
                .attr("fill", "none")
                .attr("d", area.lineY1());
           
            // Axes
            
            g.append("g")
                .attr("transform", `translate(0,${height})`)
                .call(d3.axisBottom(x));
            
            g.append("g")
                .call(d3.axisLeft(y)
                        .tickFormat(d3.format('~s'))
                        .tickSizeOuter(0));
          }
        </script>
    </body>
    </html>